summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-06-11 17:20:15 +0700
committerhathach <[email protected]>2026-06-11 17:20:15 +0700
commit5145b67f7946763ca04954f5494bfa88eec2acad (patch)
tree9130540204c2404d532613adab01e88f4e918daa
parentf52d9c7b27dc0234fb4738762a8a6da01817e2bf (diff)
parent0244a4f12e019406a4b73c75cd96f8efa096bbdc (diff)
Merge remote-tracking branch 'origin/master' into stm32c5
# Conflicts: # README.rst
-rw-r--r--.claude/skills/hil/SKILL.md66
-rw-r--r--.gitattributes3
-rw-r--r--.github/FUNDING.yml1
-rw-r--r--.github/labeler.yml77
-rw-r--r--.github/workflows/build.yml103
-rw-r--r--.github/workflows/build_util.yml2
-rw-r--r--.github/workflows/claude-code-review.yml37
-rw-r--r--.github/workflows/claude.yml47
-rw-r--r--.github/workflows/labeler.yml159
-rw-r--r--.gitignore2
-rw-r--r--AGENTS.md4
-rw-r--r--README.rst212
-rw-r--r--docs/assets/adafruit_logo.svg6
-rw-r--r--docs/assets/stack.svg2
-rwxr-xr-xdocs/conf.py19
-rw-r--r--docs/reference/device_issues.rst35
-rw-r--r--docs/reference/index.rst1
-rw-r--r--docs/troubleshooting.rst2
-rw-r--r--examples/device/audio_4_channel_mic_freertos/CMakeLists.txt5
-rw-r--r--examples/device/audio_test_freertos/CMakeLists.txt5
-rw-r--r--examples/device/board_test/CMakeLists.txt5
-rw-r--r--examples/device/board_test/src/main.c26
-rw-r--r--examples/device/cdc_msc_freertos/CMakeLists.txt5
-rw-r--r--examples/device/cdc_msc_freertos/src/main.c2
-rw-r--r--examples/device/hid_composite_freertos/CMakeLists.txt5
-rw-r--r--examples/device/midi2_device/CMakeLists.txt4
-rw-r--r--examples/device/midi2_device/src/main.c2
-rw-r--r--examples/device/msc_dual_lun/src/main.c2
-rw-r--r--examples/dual/dynamic_switch/src/main.c159
-rw-r--r--examples/dual/host_info_to_device_cdc/src/main.c6
-rw-r--r--examples/host/bare_api/src/main.c43
-rw-r--r--examples/host/cdc_msc_hid/src/cdc_app.c6
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/cdc_app.c10
-rw-r--r--examples/host/device_info/src/main.c71
-rw-r--r--examples/host/midi2_host/src/main.c4
-rw-r--r--examples/host/msc_file_explorer_freertos/skip.txt1
-rw-r--r--hw/bsp/BoardPresets.json26
-rw-r--r--hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.cmake8
-rw-r--r--hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.h27
-rw-r--r--hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.mk5
-rw-r--r--hw/bsp/ch32v10x/family.c23
-rw-r--r--hw/bsp/espressif/family.cmake7
-rw-r--r--hw/bsp/family_support.cmake62
-rw-r--r--hw/bsp/stm32f4/family.c5
-rw-r--r--hw/bsp/stm32f7/boards/stm32f746disco/board.cmake2
-rw-r--r--hw/bsp/stm32f7/boards/stm32f769disco/board.cmake1
-rw-r--r--hw/bsp/stm32f7/family.c21
-rw-r--r--hw/bsp/stm32u0/family.c13
-rw-r--r--hw/bsp/tm4c/family.cmake4
-rw-r--r--src/class/cdc/cdc_host.c92
-rw-r--r--src/class/mtp/mtp_device.c13
-rw-r--r--src/common/tusb_types.h1
-rw-r--r--src/host/usbh.c334
-rw-r--r--src/osal/osal.h39
-rw-r--r--src/osal/osal_freertos.h13
-rw-r--r--src/osal/osal_mynewt.h6
-rw-r--r--src/osal/osal_none.h16
-rw-r--r--src/osal/osal_pico.h7
-rw-r--r--src/osal/osal_rtthread.h6
-rw-r--r--src/osal/osal_rtx4.h6
-rw-r--r--src/osal/osal_threadx.h5
-rw-r--r--src/osal/osal_zephyr.h6
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c135
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c51
-rw-r--r--src/portable/wch/dcd_ch32_usbhs.c2
-rw-r--r--src/tusb.c2
-rw-r--r--src/tusb_option.h12
-rw-r--r--test/hil/hfp.json4
-rw-r--r--test/hil/hil_ci.sh68
-rw-r--r--test/hil/hil_ci_set_matrix.py26
-rwxr-xr-xtest/hil/hil_test.py297
-rw-r--r--test/hil/requirements.txt11
-rw-r--r--test/hil/tinyusb.json299
-rwxr-xr-xtools/build.py30
-rw-r--r--tools/codespell/ignore-words.txt1
-rwxr-xr-xtools/gen_presets.py4
-rwxr-xr-xtools/get_deps.py2
-rw-r--r--tools/metrics.py6
78 files changed, 2129 insertions, 708 deletions
diff --git a/.claude/skills/hil/SKILL.md b/.claude/skills/hil/SKILL.md
index 1f3d7d072..a7a916907 100644
--- a/.claude/skills/hil/SKILL.md
+++ b/.claude/skills/hil/SKILL.md
@@ -1,66 +1,64 @@
---
name: hil
-description: Use when running TinyUSB Hardware-in-the-Loop (HIL) tests on physical boards, debugging HIL failures, or copying firmware to the ci.lan test rig. Covers local execution and remote execution over SSH, config selection, and debugging tips.
+description: Use when running TinyUSB Hardware-in-the-Loop (HIL) tests on physical boards, debugging HIL failures, or copying firmware to the ci.lan test rig. Covers per-host config selection (htpc uses local.json, ci uses tinyusb.json), local execution on either htpc or ci, remote execution over SSH from htpc, and debugging tips.
---
# Hardware-in-the-Loop (HIL) Testing
-Run TinyUSB HIL tests against real boards. Two execution modes — **local** (boards attached to this machine) and **remote** (boards attached to `ci.lan`, reached over SSH). Default to **local** unless the user specifies `remote`. Do not auto-detect.
+Run TinyUSB HIL tests on real boards. **Run `hostname` first** — it tells you which host you are on, which determines the default config and whether remote mode is possible.
-## Prerequisites
+| Host | Local config | Remote (SSH → ci.lan)? |
+|------|--------------|------------------------|
+| `htpc` (dev PC) | `test/hil/local.json` | yes (large pool, `test/hil/tinyusb.json`) |
+| `ci` (the rig) | `test/hil/tinyusb.json` (large pool) | no — can't SSH to htpc, and boards are already local |
-- Examples must already be built for the target board(s). See AGENTS.md "Build" → "All examples for a board", which produces `examples/cmake-build-<board>/`.
-- `-B examples` tells `hil_test.py` that `examples/` is the parent folder containing the per-board build outputs.
+Default to **local**. Use **remote** only when on `htpc` and the user says `remote`/`ci.lan`. Never attempt remote on `ci`.
-## Choosing arguments
+## Prerequisites
-Infer from the user's request:
+Examples must be built for the target board(s) — see AGENTS.md "Build" → "All examples for a board" (produces `examples/cmake-build-<board>/`). `-B examples` points `hil_test.py` at that parent folder.
-- **Mode:** `local` (default) or `remote`. Only switch to `remote` if the user explicitly says so or names `ci.lan`.
-- **Board:** if the user names a specific board, pass `-b BOARD_NAME`. Otherwise omit `-b` to run all boards in the config.
-- **Pass-through flags:** `-v` (verbose), `-r N` (retry count), etc. — pass through unchanged.
+## Arguments
-Config file follows from mode:
-- **Local** → `test/hil/local.json` (user-supplied; not tracked in repo — describes boards attached locally)
-- **Remote** → `test/hil/tinyusb.json` (tracked; describes the `ci.lan` test rig)
+- **Board:** `-b BOARD_NAME` for one board; omit to run all boards in the config.
+- **Pass-through:** `-v`, `-r N`, etc. forwarded unchanged.
-If `local.json` is missing, fall back to `tinyusb.json` only when explicitly told to; otherwise stop and ask the user to supply one.
+If `local.json` is missing on `htpc`, ask the user to supply one (only fall back to `tinyusb.json` if told to).
## Local execution
-Boards attached to this machine:
+Set `CONFIG` from `hostname` first (`test/hil/local.json` on htpc, `test/hil/tinyusb.json` on ci):
```bash
-# Specific board:
-python3 test/hil/hil_test.py -b BOARD_NAME -B examples test/hil/local.json $EXTRA_ARGS
-# All boards in the config (no -b):
-python3 test/hil/hil_test.py -B examples test/hil/local.json $EXTRA_ARGS
+CONFIG=test/hil/local.json # on ci use: CONFIG=test/hil/tinyusb.json
+
+# All boards in the config:
+python3 test/hil/hil_test.py -B examples "$CONFIG"
+
+# A single board (replace stm32f723disco):
+python3 test/hil/hil_test.py -b stm32f723disco -B examples "$CONFIG"
```
-## Remote execution (ci.lan)
+Append pass-through flags (`-v`, `-r 1`, …) to either command as needed.
-Use `test/hil/hil_ci.sh` — it handles dir setup, scp of test scripts, rsync of firmware artifacts (`.elf` / `.bin` / `.hex` only), and running `hil_test.py` on `ci.lan`:
+## Remote execution (htpc → ci.lan only)
+
+`test/hil/hil_ci.sh` handles dir setup, scp of test scripts, rsync of firmware (`.elf`/`.bin`/`.hex`), and runs `hil_test.py` on `ci.lan` with `tinyusb.json`:
```bash
-# Specific board:
-bash test/hil/hil_ci.sh -b raspberry_pi_pico2
-# All boards in tinyusb.json:
+# All boards:
bash test/hil/hil_ci.sh
-# Pass-through extra args (any non -b flag is forwarded to hil_test.py):
+
+# A single board, with pass-through flags:
bash test/hil/hil_ci.sh -b raspberry_pi_pico2 -t host/cdc_msc_hid -r 1
```
-Overrides via env vars: `REMOTE=ci.lan`, `REMOTE_DIR=/tmp/tinyusb-hil`, `CONFIG=test/hil/tinyusb.json`.
-
-The script fails fast if the build dir or repo layout is missing.
+Env overrides: `REMOTE`, `REMOTE_DIR`, `CONFIG`. Fails fast if the build dir/repo layout is missing.
## Timing
-HIL runs take 2-5 minutes. Use a timeout of at least 20 minutes (600000 ms). NEVER cancel early.
+Runs take 2-5 min. Use a timeout ≥ 20 min (1200000 ms). NEVER cancel early.
-## Reporting results
+## Reporting
-After the test completes:
-- Show the test output to the user.
-- Summarize pass/fail per board.
-- On failure, suggest re-running with `-v` for verbose output. If `-v` isn't enough, temporarily add debug prints to `test/hil/hil_test.py` to pinpoint the issue.
+Show the output, summarize pass/fail per board. On failure, retry with `-v`; if that's not enough, add temporary debug prints to `hil_test.py`.
diff --git a/.gitattributes b/.gitattributes
index 140ae8929..723f48277 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -19,6 +19,9 @@
Makefile text
+# Shell scripts must stay LF even when core.autocrlf=true (CRLF breaks bash)
+*.sh text eol=lf
+
# Windows-only Visual Studio things
*.sln text eol=crlf
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..e9930a25a
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: hathach
diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 000000000..6c7aa7e7d
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,77 @@
+# Path-based auto-labeling for USB IP / port drivers.
+# Maps changed dcd/hcd files under src/portable/ to their "Port <ip>" label.
+# Consumed by actions/labeler (see .github/workflows/labeler.yml -> label-port job).
+
+"Port DWC2":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/synopsys/dwc2/**'
+
+"Port EHCI":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/ehci/**'
+
+"Port OHCI":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/ohci/**'
+
+"Port FSDev":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/st/stm32_fsdev/**'
+
+"Port ChipIdea":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/chipidea/**'
+
+"Port NXP IP3511":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/nxp/lpc_ip3511/**'
+
+"Port NXP IP3516":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/nxp/lpc_ip3516/**'
+
+"Port MUSB":
+ - changed-files:
+ - any-glob-to-any-file:
+ - 'src/portable/mentor/musb/**'
+ - 'src/portable/sunxi/**'
+
+"Port RUSB2":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/renesas/rusb2/**'
+
+"Port WCH USBFS":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/wch/*usbfs*'
+
+"Port WCH USBHS":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/wch/*usbhs*'
+
+"Port MAX3421":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/analog/max3421/**'
+
+"Port SAMD":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/microchip/samd/**'
+
+"Port SAMG":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/microchip/samg/**'
+
+"Port nRF":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/nordic/nrf5x/**'
+
+"Port Nuvoton":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/nuvoton/**'
+
+"Port RP2":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/raspberrypi/**'
+
+"Port MSP430":
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/ti/msp430x5xx/**'
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index af0191149..a7c7cf99a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -193,6 +193,36 @@ jobs:
path: metrics_compare.md
# ---------------------------------------
+ # Keep the metrics baseline available on no-code-change pushes
+ # The code-metrics job only runs (and uploads metrics-tinyusb) when code changed, so a
+ # workflow/docs-only push to master would leave the latest run without a baseline for PRs
+ # to compare against. Carry the previous artifact forward so the baseline is never missing.
+ # ---------------------------------------
+ metrics-carry-forward:
+ needs: [ check-paths ]
+ if: github.event_name == 'push' && needs.check-paths.outputs.code_changed != 'true'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download previous metrics baseline from this branch
+ uses: dawidd6/action-download-artifact@v11
+ with:
+ workflow: build.yml
+ workflow_conclusion: '' # any conclusion, matching the PR-side baseline download
+ search_artifacts: true # scan back past runs that lack the artifact (e.g. earlier no-code pushes)
+ branch: ${{ github.ref_name }}
+ name: metrics-tinyusb
+ path: .
+ if_no_artifact_found: warn
+ continue-on-error: true # best-effort: never make a no-code push red
+
+ - name: Re-publish baseline so the latest run keeps it
+ if: hashFiles('metrics.json') != ''
+ uses: actions/upload-artifact@v7
+ with:
+ name: metrics-tinyusb
+ path: metrics.json
+
+ # ---------------------------------------
# Build Make/CMake on Windows/MacOS
# ---------------------------------------
build-os:
@@ -276,6 +306,9 @@ jobs:
env:
HIL_JSON: ${{ matrix.hil_json }}
steps:
+ - name: Set HIL report dir (sibling of workspace; persists across run attempts)
+ run: echo "HIL_REPORT_DIR=$(dirname "$GITHUB_WORKSPACE")/hil-report" >> "$GITHUB_ENV"
+
- name: Get Skip Boards from previous run
if: github.run_attempt != '1'
run: |
@@ -314,6 +347,15 @@ jobs:
exit 1
fi)
+ - name: Upload HIL report
+ if: always() && github.event_name == 'pull_request'
+ uses: actions/upload-artifact@v7
+ with:
+ name: hil-report-${{ matrix.display }}
+ path: ${{ env.HIL_REPORT_DIR }}/hil_report.md
+ if-no-files-found: ignore
+ overwrite: true
+
# ---------------------------------------
# Hardware in the loop (HIL)
# self-hosted by HFP, build with IAR toolchain, for attached hardware checkout test/hil/hfp.json
@@ -355,8 +397,67 @@ jobs:
run: python3 tools/get_deps.py $BUILD_ARGS
- name: Build
- run: python3 tools/build.py --toolchain iar $BUILD_ARGS
+ run: |
+ # Each variant carries its own --build-name/--cflag, which are global to a
+ # single build.py invocation — so build one matrix entry at a time rather
+ # than joining them (joining would leak a variant's flags onto every board).
+ readarray -t ENTRIES < <(python test/hil/hil_ci_set_matrix.py test/hil/hfp.json | jq -r '.["arm-gcc"][]')
+ for entry in "${ENTRIES[@]}"; do
+ echo "+ tools/build.py --toolchain iar $entry"
+ python3 tools/build.py --toolchain iar $entry
+ done
- name: Test on actual hardware (hardware in the loop)
run: |
python3 test/hil/hil_test.py hfp.json
+
+ - name: Upload HIL report
+ if: always() && github.event_name == 'pull_request'
+ uses: actions/upload-artifact@v7
+ with:
+ name: hil-report-hfp-iar
+ path: hil_report.md
+ if-no-files-found: ignore
+ overwrite: true
+
+ # ---------------------------------------
+ # Combine HIL results from the rigs into a single sticky PR comment (one table per rig)
+ # ---------------------------------------
+ hil-report:
+ needs: [ hil-tinyusb, hil-hfp-iar ]
+ if: |
+ always() &&
+ (needs.hil-tinyusb.result != 'skipped' || needs.hil-hfp-iar.result != 'skipped') &&
+ github.event_name == 'pull_request' &&
+ github.repository_owner == 'hathach' &&
+ github.event.pull_request.head.repo.fork == false
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - name: Download HIL reports
+ uses: actions/download-artifact@v5
+ with:
+ pattern: hil-report-*
+ path: hil-reports
+
+ - name: Combine rig reports (one table per rig)
+ run: |
+ {
+ echo "## Hardware-in-the-loop (HIL) Test Report"
+ echo
+ for d in hil-reports/hil-report-*; do
+ [ -d "$d" ] || continue
+ echo "### ${d#hil-reports/hil-report-}"
+ echo
+ cat "$d/hil_report.md" 2>/dev/null || echo "_no report produced_"
+ echo
+ done
+ } > hil_combined.md
+ cat hil_combined.md
+
+ - name: Post HIL report as sticky PR comment
+ uses: marocchino/sticky-pull-request-comment@v2
+ with:
+ header: hil-report
+ path: hil_combined.md
diff --git a/.github/workflows/build_util.yml b/.github/workflows/build_util.yml
index 69b6f28d5..2532caebe 100644
--- a/.github/workflows/build_util.yml
+++ b/.github/workflows/build_util.yml
@@ -67,7 +67,7 @@ jobs:
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
run: |
if [ "${{ inputs.toolchain }}" == "esp-idf" ]; then
- docker run --rm -e MEMBROWSE_API_KEY="$MEMBROWSE_API_KEY" -v $PWD:/project -w /project espressif/idf:tinyusb python tools/build.py --target all ${{ matrix.arg }}
+ docker run --rm -e MEMBROWSE_API_KEY="$MEMBROWSE_API_KEY" -e CI="$CI" -v $PWD:/project -w /project espressif/idf:tinyusb python tools/build.py --target all ${{ matrix.arg }}
else
BUILD_PY_ARGS="-s ${{ inputs.build-system }} ${{ steps.setup-toolchain.outputs.build_option }} ${{ inputs.build-options }} --target all"
if [ "${{ inputs.upload-metrics }}" = "true" ]; then
diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml
index 43144bb5e..71c1cb8ab 100644
--- a/.github/workflows/claude-code-review.yml
+++ b/.github/workflows/claude-code-review.yml
@@ -1,18 +1,29 @@
name: Claude Code Review
on:
- pull_request_target:
- types: [opened, synchronize, ready_for_review, reopened]
+ pull_request:
+ # opened/reopened/ready_for_review -> first auto review
+ # synchronize -> auto re-review on new pushes
+ #
+ # NOTE: pull_request (not _target) means fork PRs get a read-only GITHUB_TOKEN
+ # and NO repository secrets (CLAUDE_CODE_OAUTH_TOKEN), so they cannot be
+ # auto-reviewed. The job condition below skips them cleanly -> use @claude on
+ # those. Same-repo branches (yours or write-access contributors) auto-review.
+ types: [opened, synchronize, reopened, ready_for_review]
jobs:
claude-review:
- if: false
+ # Skip drafts, and skip fork PRs (no secrets -> would only fail noisily)
+ if: >
+ github.event.pull_request.draft == false &&
+ github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
- issues: read
+ issues: write # Claude posts the review comment via the issues API
id-token: write
+ actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
@@ -25,8 +36,22 @@ jobs:
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
+ # Allow claude[bot]'s own pushes to be handled gracefully (skip) instead
+ # of erroring out the workflow
+ allowed_bots: 'claude'
+ # Pairs with the actions: read permission so Claude can read CI results
+ additional_permissions: |
+ actions: read
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
plugins: 'code-review@claude-code-plugins'
- prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
+ # Post/update a single summary comment every run, so a clean review
+ # ("no issues found") is still visible instead of posting nothing.
+ use_sticky_comment: true
+ # --comment makes the code-review command post its findings to the PR.
+ # Without it the command only prints the review to the Actions log.
+ prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }} --comment'
+ # TEMPORARY: expose the full Claude transcript in the Actions log for
+ # debugging. Revert to remove once done.
+ show_full_output: true
+ claude_args: '--max-turns 20'
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
- # or https://code.claude.com/docs/en/cli-reference for available options
diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml
index 50f449949..66e36897c 100644
--- a/.github/workflows/claude.yml
+++ b/.github/workflows/claude.yml
@@ -6,22 +6,32 @@ on:
pull_request_review_comment:
types: [created]
issues:
- types: [opened, assigned]
+ # only "opened" — an issue's author_association gates the summon below;
+ # "assigned" would gate on the issue author, not the assigner, so a
+ # maintainer assigning an outsider's issue would be wrongly skipped.
+ types: [opened]
pull_request_review:
types: [submitted]
jobs:
claude:
+ # Only trusted actors (repo owner/member/collaborator) may summon @claude, so the
+ # write-scoped token and OAuth secret are never issued for an outside contributor's
+ # comment on this public repo. Defense-in-depth on top of the action's own check.
if: |
- (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
- (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
- (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
- (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') &&
+ contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
+ (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') &&
+ contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
+ (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') &&
+ contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association)) ||
+ (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) &&
+ contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association))
runs-on: ubuntu-latest
permissions:
- contents: read
- pull-requests: read
- issues: read
+ contents: write # allow Claude to push commits/branches when asked
+ pull-requests: write # allow Claude to comment on / update PRs
+ issues: write # allow Claude to comment on / update issues
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
@@ -40,10 +50,19 @@ jobs:
additional_permissions: |
actions: read
- # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
- # prompt: 'Update the pull request description to include a summary of changes.'
+ # Sign the bot's commits so they show as "Verified". The action commits
+ # automatically — on a PR comment it pushes to that PR's branch; on an
+ # issue comment it opens a new claude/* branch + PR with the fix.
+ use_commit_signing: true
- # Optional: Add claude_args to customize behavior and configuration
- # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
- # or https://code.claude.com/docs/en/cli-reference for available options
- # claude_args: '--allowed-tools Bash(gh pr:*)'
+ # No custom prompt: Claude performs the instructions in the @claude comment.
+
+ # Deliberately NO Bash in the tool allowlist. @claude can be summoned on a
+ # fork PR (claude-code-review.yml even directs fork PRs here), and this job
+ # holds the OAuth secret + a write token. Any build/interpreter command
+ # (python -c, cmake/make custom targets, etc.) run against attacker-
+ # controlled PR content is arbitrary code + network execution, so no
+ # command allowlist can safely contain it. Claude still edits files and
+ # the action commits/opens the PR; the resulting commit is verified by the
+ # repo's CircleCI matrix. --max-turns gives room to investigate + fix.
+ claude_args: '--max-turns 30'
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index c3cc59d0d..fe09413f1 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -4,23 +4,29 @@ on:
issues:
types: [opened]
pull_request_target:
- types: [opened]
+ types: [opened, synchronize, reopened]
+ discussion:
+ types: [created]
jobs:
label-priority:
+ # Author-based priority labels: only on issue/PR/discussion creation, not on PR updates.
+ if: github.event_name != 'pull_request_target' || github.event.action == 'opened'
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
+ discussions: write
steps:
- - name: Label New Issue or PR
- uses: actions/github-script@v7
+ - name: Label New Issue, PR or Discussion
+ uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
- let label = '';
+ let labels = [];
let username = '';
let issueOrPrNumber = 0;
+ let discussionNodeId = '';
if (context.eventName === 'issues') {
username = context.payload.issue.user.login;
@@ -28,25 +34,85 @@ jobs:
} else if (context.eventName === 'pull_request_target') {
username = context.payload.pull_request.user.login;
issueOrPrNumber = context.payload.pull_request.number;
+ } else if (context.eventName === 'discussion') {
+ username = context.payload.discussion.user.login;
+ discussionNodeId = context.payload.discussion.node_id;
}
- // Check if an Adafruit member
- try {
- const adafruitResponse = await github.rest.orgs.checkMembershipForUser({
- org: 'adafruit',
- username: username
- });
+ // Maintainer is an Adafruit member; skip the Adafruit perks for their own
+ // issues/PRs and treat them as a plain contributor (Prio only).
+ const isOwner = username.toLowerCase() === 'hathach';
- if (adafruitResponse.status === 204) {
- console.log('Adafruit Member');
- label = 'Prio Urgent';
+ // Check if an Adafruit member: Adafruit + Sponsor + top priority
+ if (!isOwner) {
+ try {
+ const adafruitResponse = await github.rest.orgs.checkMembershipForUser({
+ org: 'adafruit',
+ username: username
+ });
+
+ if (adafruitResponse.status === 204) {
+ console.log('Adafruit Member');
+ labels = ['Adafruit 🌸', 'Sponsor 💖', 'Prio Top 🚨'];
+ }
+ } catch (error) {
+ console.log('Not an Adafruit member');
}
- } catch (error) {
- console.log('Not an Adafruit member');
}
- // Check if a contributor
- if (label == '') {
+ // Check if a public GitHub Sponsor of the repo owner.
+ // Word ($32) tier and up get triage priority; DWORD/QWORD ($128+) go to the top.
+ // Private sponsorships are not visible to GITHUB_TOKEN, so only public sponsors are detected.
+ if (labels.length === 0) {
+ try {
+ const result = await github.graphql(`
+ query($sponsorable: String!, $sponsor: String!) {
+ user(login: $sponsorable) {
+ isSponsoredBy(accountLogin: $sponsor)
+ sponsorshipsAsMaintainer(includePrivate: false, first: 100) {
+ nodes {
+ sponsorEntity {
+ ... on User { login }
+ ... on Organization { login }
+ }
+ tier { monthlyPriceInDollars }
+ }
+ }
+ }
+ }`, { sponsorable: context.repo.owner, sponsor: username });
+
+ const owner = result.user;
+ if (owner && owner.isSponsoredBy) {
+ let monthly = 0;
+ const nodes = (owner.sponsorshipsAsMaintainer && owner.sponsorshipsAsMaintainer.nodes) || [];
+ for (const node of nodes) {
+ const login = node.sponsorEntity && node.sponsorEntity.login;
+ if (login && login.toLowerCase() === username.toLowerCase()) {
+ monthly = (node.tier && node.tier.monthlyPriceInDollars) || 0;
+ break;
+ }
+ }
+
+ if (monthly >= 128) {
+ console.log('Sponsor (DWORD/QWORD tier)');
+ labels = ['Sponsor 💖', 'Prio Top 🚨'];
+ } else if (monthly >= 32) {
+ console.log('Sponsor (Word tier)');
+ labels = ['Sponsor 💖', 'Prio 📌'];
+ } else {
+ console.log('Sponsor (below Word tier or tier not visible)');
+ labels = ['Sponsor 💖'];
+ }
+ } else {
+ console.log('Not a public sponsor');
+ }
+ } catch (error) {
+ console.log('Sponsor lookup failed: ' + error.message);
+ }
+ }
+
+ // Check if a contributor: prioritized in triage queue
+ if (labels.length === 0) {
try {
const collaboratorResponse = await github.rest.repos.checkCollaborator({
owner: context.repo.owner,
@@ -56,18 +122,61 @@ jobs:
if (collaboratorResponse.status === 204) {
console.log('Contributor');
- label = 'Prio Higher';
+ labels = ['Prio 📌'];
}
} catch (error) {
console.log('Not a contributor');
}
}
- if (label !== '') {
- await github.rest.issues.addLabels({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: issueOrPrNumber,
- labels: [label]
- });
+ if (labels.length !== 0) {
+ if (context.eventName === 'discussion') {
+ // Discussions are not covered by the REST issues API; resolve the label
+ // names to node IDs and attach them with the GraphQL labelable mutation.
+ const labelIds = [];
+ for (const name of labels) {
+ const res = await github.graphql(`
+ query($owner: String!, $repo: String!, $name: String!) {
+ repository(owner: $owner, name: $repo) {
+ label(name: $name) { id }
+ }
+ }`, { owner: context.repo.owner, repo: context.repo.repo, name: name });
+ if (res.repository.label) {
+ labelIds.push(res.repository.label.id);
+ }
+ }
+ if (labelIds.length !== 0) {
+ await github.graphql(`
+ mutation($labelableId: ID!, $labelIds: [ID!]!) {
+ addLabelsToLabelable(input: { labelableId: $labelableId, labelIds: $labelIds }) {
+ clientMutationId
+ }
+ }`, { labelableId: discussionNodeId, labelIds: labelIds });
+ }
+ } else {
+ await github.rest.issues.addLabels({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: issueOrPrNumber,
+ labels: labels
+ });
+ }
}
+
+ # Path-based Port labels: attach "Port <ip>" when a PR touches the matching
+ # dcd/hcd driver under src/portable/. Mapping lives in .github/labeler.yml.
+ label-port:
+ if: github.event_name == 'pull_request_target'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+ issues: write # allow auto-creating a Port label that doesn't exist yet
+ steps:
+ - uses: actions/labeler@v5
+ with:
+ configuration-path: .github/labeler.yml
+ # sync-labels so a Port label is removed once a PR no longer touches
+ # that driver (job reruns on synchronize). Only labels listed in
+ # labeler.yml are managed, so author-based Prio/Sponsor labels are untouched.
+ sync-labels: true
diff --git a/.gitignore b/.gitignore
index c11e51bb9..0f3c9ce49 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
html
latex
+hil_report.md
+hil_report.json
*.a
*.d
*.o
diff --git a/AGENTS.md b/AGENTS.md
index 5c9908d19..93faa6332 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -18,7 +18,7 @@ Bias toward caution over speed. For trivial tasks, use judgment.
- **Language/style:** C99, 2-space indent (no tabs), snake_case helpers, `UPPER_CASE` macros. Public APIs use `tud_`/`tuh_`; macros use `TU_`. Headers self-contained with `#if CFG_TUSB_MCU` guards.
- **Safety:** no dynamic allocation; defer ISR work to task context; use `TU_ASSERT()` for error checks; always check return values; include order: C stdlib → tusb common → drivers → classes.
- **Layout:** `src/` core, `hw/{mcu,bsp}/` MCU+BSP, `examples/{device,host,dual}/`, `test/{unit-test,fuzz,hil}/`, `docs/`, `tools/`.
-- **Commits/PRs:** imperative mood, scoped changes, link issues, include test/build evidence.
+- **Commits/PRs:** imperative mood, scoped changes, link issues, include test/build evidence. After opening a PR, monitor it and drive it to green: address automated review comments (Copilot/Codex/Claude) and fix any failing CI builds, pushing follow-up commits until checks pass and review threads are resolved. Useful: `gh pr checks <num> --watch`, `gh pr view <num> --comments`.
- **Formatting/lint:** `clang-format` (`.clang-format`), `codespell` (`.codespellrc`), run `pre-commit run --all-files` before submitting.
## Bootstrap
@@ -204,7 +204,7 @@ Device examples need real hardware to validate runtime behavior; must at least b
## References
-- MCU reference manuals, datasheets, schematics: `$HOME/Documents/Calibre Library`.
+- MCU reference manuals, datasheets, schematics: `$HOME/Documents/calibre-library`.
- Supported MCUs/boards: `hw/bsp/` and `docs/reference/boards.rst`.
- USB classes: `src/class/{cdc,hid,msc,audio,…}/` — each has `*_device.c` and `*_host.c`.
- Key files: `src/tusb.h`, `src/tusb_config.h`, `tools/get_deps.py`, `tools/build.py`, `test/unit-test/project.yml`.
diff --git a/README.rst b/README.rst
index 1c56a6ba0..38a77f8f6 100644
--- a/README.rst
+++ b/README.rst
@@ -1,12 +1,32 @@
TinyUSB
=======
-|Build Status| |CircleCI Status| |Documentation Status| |Static Analysis| |Fuzzing Status| |Membrowse| |License|
+|Sponsor| |Build Status| |CircleCI Status| |Documentation Status| |Static Analysis| |Fuzzing Status| |Membrowse| |License|
+
+Overview
+--------
+
+.. figure:: docs/assets/logo.svg
+ :alt: TinyUSB
+ :align: left
+
+.. raw:: html
+
+ <div class="clear-both"></div>
+
+TinyUSB is an open-source cross-platform USB Host/Device stack for embedded systems. It’s designed for memory safety
+(no dynamic allocation) and thread safety (all interrupts deferred to non-ISR task functions). The stack emphasizes portability,
+small footprint, and real-time performance across 50+ MCU families.
Sponsors
--------
-TinyUSB is funded by: Adafruit. Purchasing products from them helps to support this project.
+TinyUSB's continued development is made possible by its sponsors. If TinyUSB helps your products or projects, consider funding its development on `GitHub Sponsors`_.
+
+Platinum
+^^^^^^^^
+
+`Adafruit`_ has backed TinyUSB since day one as its founding and sole Platinum sponsor. Buying their products directly funds the project's development.
.. figure:: docs/assets/adafruit_logo.svg
:alt: Adafruit Logo
@@ -17,20 +37,44 @@ TinyUSB is funded by: Adafruit. Purchasing products from them helps to support t
<div class="clear-both"></div>
-Overview
---------
+Sponsors (QWORD)
+^^^^^^^^^^^^^^^^
-.. figure:: docs/assets/logo.svg
- :alt: TinyUSB
- :align: left
+.. QWORD-SPONSORS-START
-.. raw:: html
+*No QWORD sponsors yet — be the first!*
- <div class="clear-both"></div>
+.. QWORD-SPONSORS-END
-TinyUSB is an open-source cross-platform USB Host/Device stack for embedded systems. It’s designed for memory safety
-(no dynamic allocation) and thread safety (all interrupts deferred to non-ISR task functions). The stack emphasizes portability,
-small footprint, and real-time performance across 50+ MCU families.
+Backers (DWORD)
+^^^^^^^^^^^^^^^
+
+.. DWORD-BACKERS-START
+
+*No backers yet — be the first!*
+
+.. DWORD-BACKERS-END
+
+Supporters (Word)
+^^^^^^^^^^^^^^^^^
+
+.. WORD-SUPPORTERS-START
+
+*No supporters yet — be the first!*
+
+.. WORD-SUPPORTERS-END
+
+Thanks (Byte)
+^^^^^^^^^^^^^
+
+.. BYTE-THANKS-START
+
+*No names listed yet — be the first!*
+
+.. BYTE-THANKS-END
+
+.. _GitHub Sponsors: https://github.com/sponsors/hathach
+.. _Adafruit: https://www.adafruit.com
Key Features
------------
@@ -130,155 +174,157 @@ TinyUSB is completely thread-safe by pushing all Interrupt Service Request (ISR)
Supported CPUs
--------------
-
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
| Manufacturer | Family | Device | Host | Highspeed | Driver | Note |
+==============+=============================+========+======+===========+========================+====================+
-| Allwinner | F1C100s/F1C200s | ✔ | | ✔ | sunxi | musb variant |
+| Allwinner | F1C100s/F1C200s | ✅ | | ✅ | sunxi | musb variant |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| Analog | MAX3421E | | ✔ | ✖ | max3421 | via SPI |
+| Analog | MAX3421E | | ✅ | ❌ | max3421 | via SPI |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | MAX32 650, 666, 690, | ✔ | | ✔ | musb | 1-dir ep |
+| | MAX32 650, 666, 690, | ✅ | | ✅ | musb | 1-dir ep |
| | MAX78002 | | | | | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| Artery AT32 | F403a_407, F413 | ✔ | | | fsdev | 512 USB RAM |
+| Artery AT32 | F403a_407, F413 | ✅ | | | fsdev | 512 USB RAM |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | F415, F435_437, F423, | ✔ | ✔ | | dwc2 | |
+| | F415, F435_437, F423, | ✅ | ✅ | | dwc2 | |
| | F425, F45x | | | | | |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | F402_F405 | ✔ | ✔ | ✔ | dwc2 | F405 is HS |
+| | F402_F405 | ✅ | ✅ | ✅ | dwc2 | F405 is HS |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| Bridgetek | FT90x | ✔ | | ✔ | ft9xx | 1-dir ep |
+| Bridgetek | FT90x | ✅ | | ✅ | ft9xx | 1-dir ep |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| Broadcom | BCM2711, BCM2837 | ✔ | | ✔ | dwc2 | |
+| Broadcom | BCM2711, BCM2837 | ✅ | | ✅ | dwc2 | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| Dialog | DA1469x | ✔ | ✖ | ✖ | da146xx | |
+| Dialog | DA1469x | ✅ | ❌ | ❌ | da146xx | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| Espressif | S2, S3, H4 | ✔ | ✔ | ✖ | dwc2 | |
+| Espressif | S2, S3, H4 | ✅ | ✅ | ❌ | dwc2 | |
| ESP32 +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | P4 | ✔ | ✔ | ✔ | dwc2 | |
+| | P4 | ✅ | ✅ | ✅ | dwc2 | |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | S31 | ✔ | ✔ | ✔ | dwc2 | |
+| | S31 | ✅ | ✅ | ✅ | dwc2 | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| GigaDevice | GD32VF103 | ✔ | | ✖ | dwc2 | |
+| GigaDevice | GD32VF103 | ✅ | | ❌ | dwc2 | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| HPMicro | HPM6750 | ✔ | ✔ | ✔ | ci_hs, ehci | |
+| HPMicro | HPM6750 | ✅ | ✅ | ✅ | ci_hs, ehci | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| Infineon | XMC4500 | ✔ | ✔ | ✖ | dwc2 | |
+| Infineon | XMC4500 | ✅ | ✅ | ❌ | dwc2 | |
+--------------+-----+-----------------------+--------+------+-----------+------------------------+--------------------+
-| MicroChip | SAM | D11, D21, L21, L22 | ✔ | | ✖ | samd | |
+| MicroChip | SAM | D11, D21, L21, L22 | ✅ | | ❌ | samd | |
| | +-----------------------+--------+------+-----------+------------------------+--------------------+
-| | | D51, E5x | ✔ | | ✖ | samd | |
+| | | D51, E5x | ✅ | | ❌ | samd | |
| | +-----------------------+--------+------+-----------+------------------------+--------------------+
-| | | G55 | ✔ | | ✖ | samg | 1-dir ep |
+| | | G55 | ✅ | | ❌ | samg | 1-dir ep |
| | +-----------------------+--------+------+-----------+------------------------+--------------------+
-| | | E70,S70,V70,V71 | ✔ | | ✔ | samx7x | 1-dir ep |
+| | | E70,S70,V70,V71 | ✅ | | ✅ | samx7x | 1-dir ep |
| +-----+-----------------------+--------+------+-----------+------------------------+--------------------+
-| | PIC | 24 | ✔ | | | pic | ci_fs variant |
+| | PIC | 24 | ✅ | | | pic | ci_fs variant |
| | +-----------------------+--------+------+-----------+------------------------+--------------------+
-| | | 32 mm, mk, mx | ✔ | | | pic | ci_fs variant |
+| | | 32 mm, mk, mx | ✅ | | | pic | ci_fs variant |
| | +-----------------------+--------+------+-----------+------------------------+--------------------+
-| | | dsPIC33 | ✔ | | | pic | ci_fs variant |
+| | | dsPIC33 | ✅ | | | pic | ci_fs variant |
| | +-----------------------+--------+------+-----------+------------------------+--------------------+
-| | | 32mz | ✔ | | | pic32mz | musb variant |
+| | | 32mz | ✅ | | | pic32mz | musb variant |
+--------------+-----+-----------------------+--------+------+-----------+------------------------+--------------------+
-| MindMotion | mm32 | ✔ | | ✖ | mm32f327x_otg | ci_fs variant |
+| MindMotion | mm32 | ✅ | | ❌ | mm32f327x_otg | ci_fs variant |
+--------------+-----+-----------------------+--------+------+-----------+------------------------+--------------------+
-| NordicSemi | nRF52, nRF53 | ✔ | ✖ | ✖ | nrf5x | only ep8 is ISO |
+| NordicSemi | nRF52, nRF53 | ✅ | ❌ | ❌ | nrf5x | only ep8 is ISO |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | nRF54 | ✔ | ✖ | ✔ | dwc2 | |
+| | nRF54 | ✅ | ❌ | ✅ | dwc2 | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| Nuvoton | NUC120 | ✔ | ✖ | ✖ | nuc120 | |
+| Nuvoton | NUC120 | ✅ | ❌ | ❌ | nuc120 | |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | NUC121/NUC125, NUC126 | ✔ | ✖ | ✖ | nuc121 | |
+| | NUC121/NUC125, NUC126 | ✅ | ❌ | ❌ | nuc121 | |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | NUC505 | ✔ | | ✔ | nuc505 | |
+| | NUC505 | ✅ | | ✅ | nuc505 | |
+--------------+---------+-------------------+--------+------+-----------+------------------------+--------------------+
-| NXP | iMXRT | RT 10xx, 11xx | ✔ | ✔ | ✔ | ci_hs, ehci | |
+| NXP | iMXRT | RT 10xx, 11xx | ✅ | ✅ | ✅ | ci_hs, ehci | |
| +---------+-------------------+--------+------+-----------+------------------------+--------------------+
-| | Kinetis | KL | ✔ | ⚠ | ✖ | ci_fs, khci | |
+| | Kinetis | KL | ✅ | 🟡 | ❌ | ci_fs, khci | |
| | +-------------------+--------+------+-----------+------------------------+--------------------+
-| | | K32L2 | ✔ | | ✖ | khci | ci_fs variant |
+| | | K32L2 | ✅ | | ❌ | khci | ci_fs variant |
| +---------+-------------------+--------+------+-----------+------------------------+--------------------+
-| | LPC | 11u, 13, 15 | ✔ | ✖ | ✖ | lpc_ip3511 | |
+| | LPC | 11u, 13, 15 | ✅ | ❌ | ❌ | lpc_ip3511 | |
| | +-------------------+--------+------+-----------+------------------------+--------------------+
-| | | 17, 40 | ✔ | ⚠ | ✖ | lpc17_40, ohci | |
+| | | 17, 40 | ✅ | 🟡 | ❌ | lpc17_40, ohci | |
| | +-------------------+--------+------+-----------+------------------------+--------------------+
-| | | 18, 43 | ✔ | ✔ | ✔ | ci_hs, ehci | |
+| | | 18, 43 | ✅ | ✅ | ✅ | ci_hs, ehci | |
| | +-------------------+--------+------+-----------+------------------------+--------------------+
-| | | 51u | ✔ | ✖ | ✖ | lpc_ip3511 | |
+| | | 51u | ✅ | ❌ | ❌ | lpc_ip3511 | |
| | +-------------------+--------+------+-----------+------------------------+--------------------+
-| | | 54 | ⚠ | ⚠ | ✔ | lpc_ip3511, lpc_ip3516 | NRND, read errata |
+| | | 54 | 🟡 | 🟡 | ✅ | lpc_ip3511, lpc_ip3516 | NRND [1]_ |
| | +-------------------+--------+------+-----------+------------------------+--------------------+
-| | | 55 | ✔ | ✔ | ✔ | lpc_ip3511, lpc_ip3516 | |
+| | | 55 | ✅ | ✅ | ✅ | lpc_ip3511, lpc_ip3516 | |
| +---------+-------------------+--------+------+-----------+------------------------+--------------------+
-| | MCX | N9 | ✔ | | ✔ | ci_fs, ci_hs, ehci | |
+| | MCX | N9 | ✅ | | ✅ | ci_fs, ci_hs, ehci | |
| | +-------------------+--------+------+-----------+------------------------+--------------------+
-| | | A15 | ✔ | | | ci_fs | |
+| | | A15 | ✅ | | | ci_fs | |
| +---------+-------------------+--------+------+-----------+------------------------+--------------------+
-| | RW61x | ✔ | ✔ | ✔ | ci_hs, ehci | |
+| | RW61x | ✅ | ✅ | ✅ | ci_hs, ehci | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| Raspberry Pi | RP2040, RP2350 | ✔ | ✔ | ✖ | rp2040, pio_usb | |
+| Raspberry Pi | RP2040, RP2350 | ✅ | ✅ | ❌ | rp2040, pio_usb | |
+--------------+-----+-----------------------+--------+------+-----------+------------------------+--------------------+
-| Renesas | RX | 63N, 65N, 72N | ✔ | ✔ | ✖ | rusb2 | |
+| Renesas | RX | 63N, 65N, 72N | ✅ | ✅ | ❌ | rusb2 | |
| +-----+-----------------------+--------+------+-----------+------------------------+--------------------+
-| | RA | 4M1, 4M3, 6M1 | ✔ | ✔ | ✖ | rusb2 | |
+| | RA | 4M1, 4M3, 6M1 | ✅ | ✅ | ❌ | rusb2 | |
| | +-----------------------+--------+------+-----------+------------------------+--------------------+
-| | | 6M5 | ✔ | ✔ | ✔ | rusb2 | |
+| | | 6M5 | ✅ | ✅ | ✅ | rusb2 | |
+--------------+-----+-----------------------+--------+------+-----------+------------------------+--------------------+
-| Silabs | EFM32GG12 | ✔ | | ✖ | dwc2 | |
+| Silabs | EFM32GG12 | ✅ | | ❌ | dwc2 | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| Sony | CXD56 | ✔ | ✖ | ✔ | cxd56 | |
+| Sony | CXD56 | ✅ | ❌ | ✅ | cxd56 | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| ST STM32 | F0, F3, L0, L1, L5, WBx5 | ✔ | ✖ | ✖ | stm32_fsdev | |
+| ST STM32 | F0, F3, L0, L1, L5, WBx5 | ✅ | ❌ | ❌ | stm32_fsdev | |
| +----+------------------------+--------+------+-----------+------------------------+--------------------+
-| | F1 | 102, 103 | ✔ | ✖ | ✖ | stm32_fsdev | 512 USB RAM |
+| | F1 | 102, 103 | ✅ | ❌ | ❌ | stm32_fsdev | 512 USB RAM |
| | +------------------------+--------+------+-----------+------------------------+--------------------+
-| | | 105, 107 | ✔ | ✔ | ✖ | dwc2 | |
+| | | 105, 107 | ✅ | ✅ | ❌ | dwc2 | |
| +----+------------------------+--------+------+-----------+------------------------+--------------------+
-| | F2, F4, F7, H7, H7RS | ✔ | ✔ | ✔ | dwc2 | |
+| | F2, F4, F7, H7, H7RS | ✅ | ✅ | ✅ | dwc2 | |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | C0, C5, G0, H5, U3 | ✔ | ✔ | ✖ | stm32_fsdev | 2KB USB RAM |
+| | C0, C5, G0, H5, U3 | ✅ | ✅ | ❌ | stm32_fsdev | 2KB USB RAM |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | G4 | ✔ | ✖ | ✖ | stm32_fsdev | 1KB USB RAM |
+| | G4 | ✅ | ❌ | ❌ | stm32_fsdev | 1KB USB RAM |
| +----+------------------------+--------+------+-----------+------------------------+--------------------+
-| | L4 | 4x2, 4x3 | ✔ | ✖ | ✖ | stm32_fsdev | 1KB USB RAM |
+| | L4 | 4x2, 4x3 | ✅ | ❌ | ❌ | stm32_fsdev | 1KB USB RAM |
| | +------------------------+--------+------+-----------+------------------------+--------------------+
-| | | 4x5, 4x6, 4+ | ✔ | ✔ | ✖ | dwc2 | |
+| | | 4x5, 4x6, 4+ | ✅ | ✅ | ❌ | dwc2 | |
| +----+------------------------+--------+------+-----------+------------------------+--------------------+
-| | N6 | ✔ | ✔ | ✔ | dwc2 | |
+| | N6 | ✅ | ✅ | ✅ | dwc2 | |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | U0 | ✔ | ✖ | ✖ | stm32_fsdev | 1KB USB RAM |
+| | U0 | ✅ | ❌ | ❌ | stm32_fsdev | 1KB USB RAM |
| +----+------------------------+--------+------+-----------+------------------------+--------------------+
-| | U5 | 535, 545 | ✔ | ✔ | ✖ | stm32_fsdev | 2KB USB RAM |
+| | U5 | 535, 545 | ✅ | ✅ | ❌ | stm32_fsdev | 2KB USB RAM |
| | +------------------------+--------+------+-----------+------------------------+--------------------+
-| | | 575, 585 | ✔ | ✔ | ✖ | dwc2 | |
+| | | 575, 585 | ✅ | ✅ | ❌ | dwc2 | |
| | +------------------------+--------+------+-----------+------------------------+--------------------+
-| | | 59x,5Ax,5Fx,5Gx | ✔ | ✔ | ✔ | dwc2 | |
+| | | 59x,5Ax,5Fx,5Gx | ✅ | ✅ | ✅ | dwc2 | |
+--------------+----+------------------------+--------+------+-----------+------------------------+--------------------+
-| TI | MSP430 | ✔ | ✖ | ✖ | msp430x5xx | |
+| TI | MSP430 | ✅ | ❌ | ❌ | msp430x5xx | |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | MSP432E4, TM4C123 | ✔ | | ✖ | musb | |
+| | MSP432E4, TM4C123 | ✅ | | ❌ | musb | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| ValentyUSB | eptri | ✔ | ✖ | ✖ | eptri | |
+| ValentyUSB | eptri | ✅ | ❌ | ❌ | eptri | |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
-| WCH | CH32F20x | ✔ | | ✔ | ch32_usbhs | |
+| WCH | CH32F20x | 🟡 | | ✅ | ch32_usbhs | ISO data loss [2]_ |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | CH32V20x | ✔ | | ✖ | stm32_fsdev/ch32_usbfs | |
+| | CH32V20x | 🟡 | | ❌ | stm32_fsdev/ch32_usbfs | ISO data loss [2]_ |
| +-----------------------------+--------+------+-----------+------------------------+--------------------+
-| | CH32V305, CH32V307 | ✔ | | ✔ | ch32_usbfs/hs | |
+| | CH32V305, CH32V307 | 🟡 | | ✅ | ch32_usbfs/hs | ISO data loss [2]_ |
+--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+
Table Legend
^^^^^^^^^^^^
========= =========================
-✔ Supported
-⚠ Partial support
-✖ Not supported by hardware
+✅ Supported
+🟡 Partial support
+❌ Not supported by hardware
\[empty\] Unknown
========= =========================
+.. [1] NRND (Not Recommended for New Design), see `NXP LPC54600 issues <docs/reference/device_issues.rst#nxp-lpc54600>`_
+.. [2] ISO data loss, see `WCH CH32F20x/CH32V20x/CH32V30x issues <docs/reference/device_issues.rst#wch-ch32f20x-ch32v20x-ch32v30x>`_
+
Development Tools
-----------------
@@ -303,6 +349,8 @@ The following tools are provided freely to support the development of the TinyUS
:target: https://membrowse.com/public/hathach/tinyusb
.. |License| image:: https://img.shields.io/badge/license-MIT-brightgreen.svg
:target: https://opensource.org/licenses/MIT
+.. |Sponsor| image:: https://img.shields.io/badge/sponsor-%E2%9D%A4-ec6cb9.svg
+ :target: https://github.com/sponsors/hathach
.. _Changelog: docs/info/changelog.rst
diff --git a/docs/assets/adafruit_logo.svg b/docs/assets/adafruit_logo.svg
index cafd5a10e..e978f2c6e 100644
--- a/docs/assets/adafruit_logo.svg
+++ b/docs/assets/adafruit_logo.svg
@@ -1,8 +1,8 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" style="enable-background:new 0 0 1382 768;" xml:space="preserve" width="144" height="80" viewBox="14 12.81 1350 455.81">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" style="enable-background:new 0 0 1382 768;" xml:space="preserve" width="224" height="89.6" viewBox="-56 -57.19 1490 595.81">
<style type="text/css">
- .st0{fill:#0;}
+ .st0{fill:#000;}
</style>
-<rect x="0" y="0" width="1382" height="768" fill="#fff"/>
+<rect x="-56" y="-57.19" width="1490" height="595.81" rx="60" ry="60" fill="#fff"/>
<g>
<g>
<g>
diff --git a/docs/assets/stack.svg b/docs/assets/stack.svg
index ed46c8649..113c92b60 100644
--- a/docs/assets/stack.svg
+++ b/docs/assets/stack.svg
@@ -1 +1 @@
-<svg version="1.1" viewBox="0.0 0.0 510.73490813648294 310.08923884514434" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l510.7349 0l0 310.08923l-510.7349 0l0 -310.08923z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#ffffff" d="m0 0l510.7349 0l0 310.08923l-510.7349 0z" fill-rule="evenodd"/><path fill="#cfe2f3" d="m28.509478 231.92447l192.28346 0l0 48.629913l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.509478 231.92447l192.28346 0l0 48.629913l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m55.37576 252.31943l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0zm9.8967705 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm6.791733 1.53125l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.65625 1.109375l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm12.972076 -2.515625l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.4592743 -0.125q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307358 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm10.963608 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2438507 1.15625l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.4118958 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.3906326 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.5937576 0 -2.5781326 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.6093826 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.9687576 0 -1.6093826 0.71875q-0.625 0.71875 -0.625 2.1875zm7.2761154 3.796875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm3.2561493 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.490524 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166733 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm9.124496 0l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm9.021759 1.234375l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.8962708 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm5.3655396 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.1667175 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0z" fill-rule="nonzero"/><path fill="#000000" d="m109.94667 270.31943l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0zm18.021774 -3.671875l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm3.0998993 3.671875l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625z" fill-rule="nonzero"/><path fill="#d9ead3" d="m220.78944 231.92447l192.28346 0l0 48.629913l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m220.78944 231.92447l192.28346 0l0 48.629913l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m240.27715 252.31943l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm14.240524 -1.21875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm9.291718 4.53125l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm5.21875 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm8.20929 -2.78125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm7.578125 0.328125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm18.894958 0.859375l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.459259 -0.125q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307373 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm10.963593 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.243866 1.15625l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.4118958 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.2760925 3.796875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm3.2561646 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.490509 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm9.124481 0l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm9.02179 1.234375l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.8962708 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm5.365509 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0z" fill-rule="nonzero"/><path fill="#000000" d="m302.17975 270.31943l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm16.67804 -2.4375l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm3.099884 3.671875l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625z" fill-rule="nonzero"/><path fill="#d9ead3" d="m220.78944 139.45203l192.28346 0l0 48.62993l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m220.78944 139.45203l192.28346 0l0 48.62993l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m263.86716 149.36261l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.2249146 7.109375l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm10.197479 3.375l0 -10.484375l3.9375 0q1.203125 0 1.921875 0.3125q0.734375 0.3125 1.140625 0.984375q0.40625 0.65625 0.40625 1.375q0 0.671875 -0.375 1.265625q-0.359375 0.59375 -1.09375 0.96875q0.953125 0.28125 1.46875 0.953125q0.515625 0.671875 0.515625 1.578125q0 0.75 -0.3125 1.390625q-0.3125 0.625 -0.78125 0.96875q-0.453125 0.34375 -1.140625 0.515625q-0.6875 0.171875 -1.6875 0.171875l-4.0 0zm1.390625 -6.078125l2.265625 0q0.921875 0 1.3125 -0.125q0.53125 -0.15625 0.796875 -0.515625q0.28125 -0.375 0.28125 -0.921875q0 -0.53125 -0.25 -0.921875q-0.25 -0.390625 -0.71875 -0.53125q-0.46875 -0.15625 -1.59375 -0.15625l-2.09375 0l0 3.171875zm0 4.84375l2.609375 0q0.671875 0 0.9375 -0.046875q0.484375 -0.09375 0.796875 -0.296875q0.328125 -0.203125 0.53125 -0.578125q0.21875 -0.390625 0.21875 -0.890625q0 -0.578125 -0.3125 -1.015625q-0.296875 -0.4375 -0.828125 -0.609375q-0.53125 -0.171875 -1.53125 -0.171875l-2.421875 0l0 3.609375zm12.503815 1.234375l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm14.24054 -1.21875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm9.291718 4.53125l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm5.21875 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm8.20929 -2.78125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm7.578125 0.328125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm11.4105835 4.53125l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm9.021759 1.234375l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.8962708 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm5.365509 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0z" fill-rule="nonzero"/><path fill="#000000" d="m304.5869 167.36261l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.2249146 7.109375l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm10.197479 3.375l0 -10.484375l3.9375 0q1.203125 0 1.921875 0.3125q0.734375 0.3125 1.140625 0.984375q0.40625 0.65625 0.40625 1.375q0 0.671875 -0.375 1.265625q-0.359375 0.59375 -1.09375 0.96875q0.953125 0.28125 1.46875 0.953125q0.515625 0.671875 0.515625 1.578125q0 0.75 -0.3125 1.390625q-0.3125 0.625 -0.78125 0.96875q-0.453125 0.34375 -1.140625 0.515625q-0.6875 0.171875 -1.6875 0.171875l-4.0 0zm1.390625 -6.078125l2.265625 0q0.921875 0 1.3125 -0.125q0.53125 -0.15625 0.796875 -0.515625q0.28125 -0.375 0.28125 -0.921875q0 -0.53125 -0.25 -0.921875q-0.25 -0.390625 -0.71875 -0.53125q-0.46875 -0.15625 -1.59375 -0.15625l-2.09375 0l0 3.171875zm0 4.84375l2.609375 0q0.671875 0 0.9375 -0.046875q0.484375 -0.09375 0.796875 -0.296875q0.328125 -0.203125 0.53125 -0.578125q0.21875 -0.390625 0.21875 -0.890625q0 -0.578125 -0.3125 -1.015625q-0.296875 -0.4375 -0.828125 -0.609375q-0.53125 -0.171875 -1.53125 -0.171875l-2.421875 0l0 3.609375zm8.431854 1.234375l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625z" fill-rule="nonzero"/><path fill="#cfe2f3" d="m28.509478 139.45203l192.28346 0l0 48.62993l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.509478 139.45203l192.28346 0l0 48.62993l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m78.9189 149.36261l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.2248993 7.109375l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm10.197479 3.375l0 -10.484375l3.9375 0q1.203125 0 1.921875 0.3125q0.734375 0.3125 1.140625 0.984375q0.40625 0.65625 0.40625 1.375q0 0.671875 -0.375 1.265625q-0.359375 0.59375 -1.09375 0.96875q0.953125 0.28125 1.46875 0.953125q0.515625 0.671875 0.515625 1.578125q0 0.75 -0.3125 1.390625q-0.3125 0.625 -0.78125 0.96875q-0.453125 0.34375 -1.140625 0.515625q-0.6875 0.171875 -1.6875 0.171875l-4.0 0zm1.390625 -6.078125l2.265625 0q0.921875 0 1.3125 -0.125q0.53125 -0.15625 0.796875 -0.515625q0.28125 -0.375 0.28125 -0.921875q0 -0.53125 -0.25 -0.921875q-0.25 -0.390625 -0.71875 -0.53125q-0.46875 -0.15625 -1.59375 -0.15625l-2.09375 0l0 3.171875zm0 4.84375l2.609375 0q0.671875 0 0.9375 -0.046875q0.484375 -0.09375 0.796875 -0.296875q0.328125 -0.203125 0.53125 -0.578125q0.21875 -0.390625 0.21875 -0.890625q0 -0.578125 -0.3125 -1.015625q-0.296875 -0.4375 -0.828125 -0.609375q-0.53125 -0.171875 -1.53125 -0.171875l-2.421875 0l0 3.609375zm12.550705 1.234375l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0zm9.896774 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm6.791733 1.53125l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.65625 1.109375l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm5.4877014 1.15625l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm9.021774 1.234375l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.8962708 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm5.3655243 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166733 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0z" fill-rule="nonzero"/><path fill="#000000" d="m112.30695 167.36261l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.2248993 7.109375l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm10.197479 3.375l0 -10.484375l3.9375076 0q1.203125 0 1.921875 0.3125q0.734375 0.3125 1.140625 0.984375q0.40625 0.65625 0.40625 1.375q0 0.671875 -0.375 1.265625q-0.359375 0.59375 -1.09375 0.96875q0.953125 0.28125 1.46875 0.953125q0.515625 0.671875 0.515625 1.578125q0 0.75 -0.3125 1.390625q-0.3125 0.625 -0.78125 0.96875q-0.453125 0.34375 -1.140625 0.515625q-0.6875 0.171875 -1.6875 0.171875l-4.0000076 0zm1.390625 -6.078125l2.2656326 0q0.921875 0 1.3125 -0.125q0.53125 -0.15625 0.796875 -0.515625q0.28125 -0.375 0.28125 -0.921875q0 -0.53125 -0.25 -0.921875q-0.25 -0.390625 -0.71875 -0.53125q-0.46875 -0.15625 -1.59375 -0.15625l-2.0937576 0l0 3.171875zm0 4.84375l2.6093826 0q0.671875 0 0.9375 -0.046875q0.484375 -0.09375 0.796875 -0.296875q0.328125 -0.203125 0.53125 -0.578125q0.21875 -0.390625 0.21875 -0.890625q0 -0.578125 -0.3125 -1.015625q-0.296875 -0.4375 -0.828125 -0.609375q-0.53125 -0.171875 -1.53125 -0.171875l-2.4218826 0l0 3.609375zm8.478737 1.234375l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m28.509478 27.969963l384.56693 0l0 48.62992l-384.56693 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.509478 27.969963l384.56693 0l0 48.62992l-384.56693 0z" fill-rule="evenodd"/><path fill="#000000" d="m184.92747 57.36492l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm7.7443542 7.21875l0 -10.5l1.171875 0l0 0.984375q0.421875 -0.578125 0.9375 -0.859375q0.515625 -0.296875 1.265625 -0.296875q0.96875 0 1.71875 0.5q0.75 0.5 1.125 1.421875q0.375 0.90625 0.375 1.984375q0 1.171875 -0.421875 2.109375q-0.40625 0.921875 -1.21875 1.421875q-0.796875 0.5 -1.671875 0.5q-0.640625 0 -1.15625 -0.265625q-0.515625 -0.28125 -0.84375 -0.6875l0 3.6875l-1.28125 0zm1.15625 -6.65625q0 1.453125 0.59375 2.15625q0.609375 0.703125 1.453125 0.703125q0.859375 0 1.46875 -0.71875q0.609375 -0.734375 0.609375 -2.25q0 -1.453125 -0.609375 -2.171875q-0.59375 -0.734375 -1.421875 -0.734375q-0.8125 0 -1.453125 0.78125q-0.640625 0.765625 -0.640625 2.234375zm6.994858 6.65625l0 -10.5l1.171875 0l0 0.984375q0.421875 -0.578125 0.9375 -0.859375q0.515625 -0.296875 1.265625 -0.296875q0.96875 0 1.71875 0.5q0.75 0.5 1.125 1.421875q0.375 0.90625 0.375 1.984375q0 1.171875 -0.421875 2.109375q-0.40625 0.921875 -1.21875 1.421875q-0.796875 0.5 -1.671875 0.5q-0.640625 0 -1.15625 -0.265625q-0.515625 -0.28125 -0.84375 -0.6875l0 3.6875l-1.28125 0zm1.15625 -6.65625q0 1.453125 0.59375 2.15625q0.609375 0.703125 1.453125 0.703125q0.859375 0 1.46875 -0.71875q0.609375 -0.734375 0.609375 -2.25q0 -1.453125 -0.609375 -2.171875q-0.59375 -0.734375 -1.421875 -0.734375q-0.8125 0 -1.453125 0.78125q-0.640625 0.765625 -0.640625 2.234375zm6.963608 3.75l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm3.2873993 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm8.209274 -2.78125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm7.328125 1.84375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm6.119858 2.65625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594757 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717743 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307358 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0z" fill-rule="nonzero"/><path fill="#d9d2e9" d="m28.509478 195.19305l384.56693 0l0 29.606308l-384.56693 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.509478 195.19305l384.56693 0l0 29.606308l-384.56693 0z" fill-rule="evenodd"/><path fill="#000000" d="m125.783455 209.96683q0 -2.609375 1.40625 -4.078125q1.4062576 -1.484375 3.6250076 -1.484375q1.453125 0 2.609375 0.703125q1.171875 0.6875 1.78125 1.921875q0.609375 1.234375 0.609375 2.8125q0 1.59375 -0.640625 2.859375q-0.640625 1.265625 -1.828125 1.90625q-1.171875 0.640625 -2.546875 0.640625q-1.46875 0 -2.640625 -0.703125q-1.1718826 -0.71875 -1.7812576 -1.953125q-0.59375 -1.25 -0.59375 -2.625zm1.4375 0.015625q0 1.90625 1.0156326 3.0q1.015625 1.078125 2.5625 1.078125q1.5625 0 2.578125 -1.09375q1.015625 -1.109375 1.015625 -3.125q0 -1.28125 -0.4375 -2.234375q-0.4375 -0.96875 -1.265625 -1.484375q-0.828125 -0.53125 -1.875 -0.53125q-1.46875 0 -2.53125 1.015625q-1.0625076 1.015625 -1.0625076 3.375zm9.915733 1.71875l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm12.366455 3.375l4.015625 -10.484375l1.5 0l4.2968597 10.484375l-1.578125 0l-1.2343597 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm8.931839 4.3125l-1.203125 0l0 -10.484375l1.296875 0l0 3.734375q0.8125 -1.015625 2.078125 -1.015625q0.703125 0 1.328125 0.28125q0.625 0.28125 1.03125 0.796875q0.40625 0.5 0.625 1.234375q0.234375 0.71875 0.234375 1.53125q0 1.96875 -0.96875 3.03125q-0.953125 1.0625 -2.3125 1.0625q-1.34375 0 -2.109375 -1.125l0 0.953125zm-0.015625 -3.859375q0 1.375 0.375 1.984375q0.609375 0.984375 1.640625 0.984375q0.84375 0 1.453125 -0.734375q0.625 -0.734375 0.625 -2.1875q0 -1.484375 -0.59375 -2.1875q-0.59375 -0.71875 -1.421875 -0.71875q-0.84375 0 -1.46875 0.734375q-0.609375 0.734375 -0.609375 2.125zm6.463623 1.59375l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.65625 1.109375l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2438354 1.15625l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm9.849396 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm8.260498 1.03125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm5.1875 1.625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594604 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717896 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.3073425 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm12.3324585 0l0 -10.484375l1.390625 0l0 9.25l5.15625 0l0 1.234375l-6.546875 0zm12.994873 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm3.2448425 6.734375l-0.140625 -1.203125q0.421875 0.109375 0.734375 0.109375q0.4375 0 0.6875 -0.140625q0.265625 -0.140625 0.421875 -0.40625q0.125 -0.1875 0.390625 -0.953125q0.046875 -0.109375 0.125 -0.3125l-2.890625 -7.609375l1.390625 0l1.578125 4.390625q0.3125 0.84375 0.546875 1.765625q0.234375 -0.890625 0.53125 -1.734375l1.625 -4.421875l1.296875 0l-2.890625 7.71875q-0.46875 1.25 -0.734375 1.734375q-0.34375 0.625 -0.78125 0.921875q-0.4375 0.296875 -1.0625 0.296875q-0.375 0 -0.828125 -0.15625zm12.593765 -5.375l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.1718903 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.5156403 0 2.4843903 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.6562653 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.50001526 -0.375 0.79689026 -1.203125zm-4.2343903 -2.078125l4.2500153 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.62501526 -0.75 -1.6093903 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.1667633 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm11.421356 3.078125q-1.0625 -1.34375 -1.796875 -3.140625q-0.734375 -1.8125 -0.734375 -3.734375q0 -1.703125 0.546875 -3.265625q0.640625 -1.8125 1.984375 -3.609375l0.921875 0q-0.859375 1.484375 -1.140625 2.125q-0.4375 0.984375 -0.6875 2.0625q-0.296875 1.34375 -0.296875 2.6875q0 3.453125 2.125 6.875l-0.921875 0zm2.1618958 -8.1875q0 -2.609375 1.40625 -4.078125q1.40625 -1.484375 3.625 -1.484375q1.453125 0 2.609375 0.703125q1.171875 0.6875 1.78125 1.921875q0.609375 1.234375 0.609375 2.8125q0 1.59375 -0.640625 2.859375q-0.640625 1.265625 -1.828125 1.90625q-1.171875 0.640625 -2.546875 0.640625q-1.46875 0 -2.640625 -0.703125q-1.171875 -0.71875 -1.78125 -1.953125q-0.59375 -1.25 -0.59375 -2.625zm1.4375 0.015625q0 1.90625 1.015625 3.0q1.015625 1.078125 2.5625 1.078125q1.5625 0 2.578125 -1.09375q1.015625 -1.109375 1.015625 -3.125q0 -1.28125 -0.4375 -2.234375q-0.4375 -0.96875 -1.265625 -1.484375q-0.828125 -0.53125 -1.875 -0.53125q-1.46875 0 -2.53125 1.015625q-1.0625 1.015625 -1.0625 3.375zm9.915741 1.71875l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm9.103729 3.375l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm7.8537292 4.3125l0 -10.484375l1.390625 0l0 9.25l5.15625 0l0 1.234375l-6.546875 0zm8.885468 3.078125l-0.921875 0q2.140625 -3.421875 2.140625 -6.875q0 -1.34375 -0.3125 -2.671875q-0.25 -1.0625 -0.671875 -2.046875q-0.28125 -0.65625 -1.15625 -2.15625l0.921875 0q1.34375 1.796875 1.984375 3.609375q0.546875 1.5625 0.546875 3.265625q0 1.921875 -0.734375 3.734375q-0.734375 1.796875 -1.796875 3.140625z" fill-rule="nonzero"/><path fill="#cfe2f3" d="m28.509478 83.71099l192.28346 0l0 48.62992l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.509478 83.71099l192.28346 0l0 48.62992l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m76.95251 104.10596l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0zm9.896774 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm6.791733 1.53125l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.65625 1.109375l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm12.972076 -2.515625l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.9123993 3.671875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.240532 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.6562576 0.25 -1.4218826 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625076 -0.1875 2.2968826 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.4531326 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.7187576 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.0937576 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.7343826 0 1.3125076 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm2.7917328 1.546875l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm7.328125 0l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.122101 2.265625l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm7.9006042 4.3125l0 -10.484375l3.96875 0q1.046875 0 1.59375 0.09375q0.765625 0.125 1.28125 0.484375q0.53125 0.359375 0.84375 1.015625q0.328125 0.65625 0.328125 1.4375q0 1.328125 -0.859375 2.265625q-0.84375 0.921875 -3.078125 0.921875l-2.6875 0l0 4.265625l-1.390625 0zm1.390625 -5.5l2.71875 0q1.34375 0 1.90625 -0.5q0.5625 -0.5 0.5625 -1.40625q0 -0.671875 -0.328125 -1.140625q-0.328125 -0.46875 -0.875 -0.609375q-0.359375 -0.09375 -1.296875 -0.09375l-2.6875 0l0 3.75zm8.619354 5.5l0 -10.484375l1.390625 0l0 10.484375l-1.390625 0z" fill-rule="nonzero"/><path fill="#000000" d="m113.3875 122.10595l0 -9.25l-3.453125 0l0 -1.234375l8.3125 0l0 1.234375l-3.46875 0l0 9.25l-1.390625 0zm13.171371 -10.484375l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.7405243 10.484375l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0z" fill-rule="nonzero"/><path fill="#d9ead3" d="m220.78944 83.71099l192.28346 0l0 48.62992l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m220.78944 83.71099l192.28346 0l0 48.62992l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m261.85388 104.10596l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm14.24054 -1.21875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm9.291718 4.53125l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm5.21875 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm8.20929 -2.78125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm7.578125 0.328125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm18.894958 0.859375l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.912384 3.671875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.24054 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm2.7917175 1.546875l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm7.328125 0l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.122101 2.265625l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm7.9006042 4.3125l0 -10.484375l3.96875 0q1.046875 0 1.59375 0.09375q0.765625 0.125 1.28125 0.484375q0.53125 0.359375 0.84375 1.015625q0.328125 0.65625 0.328125 1.4375q0 1.328125 -0.859375 2.265625q-0.84375 0.921875 -3.078125 0.921875l-2.6875 0l0 4.265625l-1.390625 0zm1.390625 -5.5l2.71875 0q1.34375 0 1.90625 -0.5q0.5625 -0.5 0.5625 -1.40625q0 -0.671875 -0.328125 -1.140625q-0.328125 -0.46875 -0.875 -0.609375q-0.359375 -0.09375 -1.296875 -0.09375l-2.6875 0l0 3.75zm8.619354 5.5l0 -10.484375l1.390625 0l0 10.484375l-1.390625 0z" fill-rule="nonzero"/><path fill="#000000" d="m305.66745 122.10595l0 -9.25l-3.453125 0l0 -1.234375l8.3125 0l0 1.234375l-3.46875 0l0 9.25l-1.390625 0zm13.171387 -10.484375l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.693634 10.484375l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m414.7422 230.80695l0 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m414.7422 230.80695l0 0" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m416.97958 84.35958l69.102356 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="8.0,3.0" d="m416.97958 84.35958l69.102356 0" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m430.57208 84.35958l69.10239 0l0 37.5748l-69.10239 0z" fill-rule="evenodd"/><path fill="#000000" d="m441.68146 103.87958l0 -7.578125l-2.828125 0l0 -1.015625l6.8125 0l0 1.015625l-2.84375 0l0 7.578125l-1.140625 0zm4.5722656 -7.375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6660156 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.626953 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm11.8125 -10.984375l1.140625 0l0 4.96875q0 1.296875 -0.296875 2.0625q-0.296875 0.75 -1.0625 1.234375q-0.765625 0.46875 -2.0 0.46875q-1.203125 0 -1.984375 -0.40625q-0.765625 -0.421875 -1.09375 -1.203125q-0.328125 -0.796875 -0.328125 -2.15625l0 -4.96875l1.140625 0l0 4.953125q0 1.125 0.203125 1.65625q0.21875 0.53125 0.71875 0.828125q0.515625 0.28125 1.25 0.28125q1.25 0 1.78125 -0.5625q0.53125 -0.578125 0.53125 -2.203125l0 -4.953125zm2.6503906 5.828125l1.0625 -0.09375q0.078125 0.65625 0.359375 1.0625q0.28125 0.40625 0.859375 0.671875q0.59375 0.25 1.328125 0.25q0.640625 0 1.140625 -0.1875q0.5 -0.203125 0.734375 -0.53125q0.25 -0.34375 0.25 -0.734375q0 -0.40625 -0.234375 -0.703125q-0.234375 -0.3125 -0.765625 -0.515625q-0.359375 -0.140625 -1.546875 -0.421875q-1.171875 -0.28125 -1.640625 -0.53125q-0.625 -0.328125 -0.921875 -0.796875q-0.296875 -0.484375 -0.296875 -1.078125q0 -0.640625 0.359375 -1.203125q0.375 -0.578125 1.078125 -0.859375q0.71875 -0.296875 1.578125 -0.296875q0.953125 0 1.6875 0.3125q0.734375 0.296875 1.125 0.90625q0.390625 0.59375 0.421875 1.34375l-1.09375 0.078125q-0.09375 -0.8125 -0.609375 -1.21875q-0.5 -0.421875 -1.484375 -0.421875q-1.03125 0 -1.5 0.375q-0.46875 0.375 -0.46875 0.90625q0 0.46875 0.328125 0.765625q0.328125 0.296875 1.703125 0.609375q1.390625 0.3125 1.90625 0.546875q0.75 0.359375 1.109375 0.890625q0.359375 0.515625 0.359375 1.21875q0 0.6875 -0.390625 1.296875q-0.390625 0.59375 -1.125 0.9375q-0.734375 0.328125 -1.65625 0.328125q-1.171875 0 -1.96875 -0.328125q-0.78125 -0.34375 -1.234375 -1.03125q-0.4375 -0.6875 -0.453125 -1.546875zm8.332031 2.765625l0 -8.59375l3.234375 0q0.984375 0 1.578125 0.265625q0.59375 0.265625 0.921875 0.8125q0.34375 0.53125 0.34375 1.125q0 0.546875 -0.296875 1.03125q-0.296875 0.484375 -0.90625 0.78125q0.78125 0.234375 1.1875 0.796875q0.421875 0.546875 0.421875 1.296875q0 0.59375 -0.25 1.125q-0.25 0.515625 -0.625 0.796875q-0.375 0.28125 -0.953125 0.421875q-0.5625 0.140625 -1.375 0.140625l-3.28125 0zm1.140625 -4.984375l1.859375 0q0.75 0 1.078125 -0.09375q0.4375 -0.125 0.65625 -0.421875q0.21875 -0.3125 0.21875 -0.765625q0 -0.421875 -0.203125 -0.75q-0.203125 -0.328125 -0.59375 -0.4375q-0.375 -0.125 -1.296875 -0.125l-1.71875 0l0 2.59375zm0 3.96875l2.140625 0q0.546875 0 0.765625 -0.046875q0.40625 -0.0625 0.65625 -0.21875q0.265625 -0.171875 0.4375 -0.484375q0.171875 -0.3125 0.171875 -0.71875q0 -0.484375 -0.25 -0.84375q-0.234375 -0.359375 -0.671875 -0.5q-0.4375 -0.140625 -1.265625 -0.140625l-1.984375 0l0 2.953125z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m416.97958 231.92447l69.102356 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="8.0,3.0" d="m416.97958 231.92447l69.102356 0" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m429.0601 231.92447l72.12598 0l0 37.5748l-72.12598 0z" fill-rule="evenodd"/><path fill="#000000" d="m437.9507 251.44447l0 -8.59375l1.71875 0l2.03125 6.078125q0.28125 0.859375 0.40625 1.28125q0.140625 -0.46875 0.453125 -1.375l2.0625 -5.984375l1.53125 0l0 8.59375l-1.09375 0l0 -7.1875l-2.5 7.1875l-1.03125 0l-2.484375 -7.3125l0 7.3125l-1.09375 0zm16.167969 -3.015625l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm8.166016 -5.578125l1.140625 0l0 4.96875q0 1.296875 -0.296875 2.0625q-0.296875 0.75 -1.0625 1.234375q-0.765625 0.46875 -2.0 0.46875q-1.203125 0 -1.984375 -0.40625q-0.765625 -0.421875 -1.09375 -1.203125q-0.328125 -0.796875 -0.328125 -2.15625l0 -4.96875l1.140625 0l0 4.953125q0 1.125 0.203125 1.65625q0.21875 0.53125 0.71875 0.828125q0.515625 0.28125 1.25 0.28125q1.25 0 1.78125 -0.5625q0.53125 -0.578125 0.53125 -2.203125l0 -4.953125zm6.359375 8.59375l0 -8.59375l3.25 0q0.84375 0 1.296875 0.078125q0.640625 0.109375 1.0625 0.40625q0.4375 0.296875 0.6875 0.828125q0.265625 0.53125 0.265625 1.171875q0 1.09375 -0.703125 1.859375q-0.6875 0.75 -2.515625 0.75l-2.203125 0l0 3.5l-1.140625 0zm1.140625 -4.5l2.21875 0q1.109375 0 1.5625 -0.40625q0.46875 -0.421875 0.46875 -1.171875q0 -0.53125 -0.28125 -0.90625q-0.265625 -0.390625 -0.703125 -0.515625q-0.296875 -0.078125 -1.0625 -0.078125l-2.203125 0l0 3.078125zm6.3476562 1.390625q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.970703 3.109375l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm6.3085938 -0.9375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125z" fill-rule="nonzero"/></g></svg>
+<svg version="1.1" viewBox="0.0 0.0 510.73490813648294 310.08923884514434" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><rect x="0" y="0" width="510.73490813648294" height="310.08923884514434" rx="20" ry="20"/></clipPath><g clip-path="url(#p.0)"><rect fill="#ffffff" x="0" y="0" width="510.73490813648294" height="310.08923884514434" rx="20" ry="20"/><path fill="#cfe2f3" d="m28.509478 231.92447l192.28346 0l0 48.629913l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.509478 231.92447l192.28346 0l0 48.629913l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m55.37576 252.31943l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0zm9.8967705 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm6.791733 1.53125l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.65625 1.109375l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm12.972076 -2.515625l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.4592743 -0.125q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307358 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm10.963608 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2438507 1.15625l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.4118958 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.3906326 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.5937576 0 -2.5781326 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.6093826 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.9687576 0 -1.6093826 0.71875q-0.625 0.71875 -0.625 2.1875zm7.2761154 3.796875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm3.2561493 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.490524 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166733 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm9.124496 0l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm9.021759 1.234375l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.8962708 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm5.3655396 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.1667175 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0z" fill-rule="nonzero"/><path fill="#000000" d="m109.94667 270.31943l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0zm18.021774 -3.671875l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm3.0998993 3.671875l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625z" fill-rule="nonzero"/><path fill="#d9ead3" d="m220.78944 231.92447l192.28346 0l0 48.629913l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m220.78944 231.92447l192.28346 0l0 48.629913l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m240.27715 252.31943l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm14.240524 -1.21875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm9.291718 4.53125l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm5.21875 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm8.20929 -2.78125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm7.578125 0.328125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm18.894958 0.859375l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.459259 -0.125q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307373 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm10.963593 -1.15625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.243866 1.15625l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.4118958 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.2760925 3.796875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm3.2561646 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.490509 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm9.124481 0l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm9.02179 1.234375l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.8962708 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm5.365509 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0z" fill-rule="nonzero"/><path fill="#000000" d="m302.17975 270.31943l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm16.67804 -2.4375l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm3.099884 3.671875l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625z" fill-rule="nonzero"/><path fill="#d9ead3" d="m220.78944 139.45203l192.28346 0l0 48.62993l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m220.78944 139.45203l192.28346 0l0 48.62993l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m263.86716 149.36261l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.2249146 7.109375l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm10.197479 3.375l0 -10.484375l3.9375 0q1.203125 0 1.921875 0.3125q0.734375 0.3125 1.140625 0.984375q0.40625 0.65625 0.40625 1.375q0 0.671875 -0.375 1.265625q-0.359375 0.59375 -1.09375 0.96875q0.953125 0.28125 1.46875 0.953125q0.515625 0.671875 0.515625 1.578125q0 0.75 -0.3125 1.390625q-0.3125 0.625 -0.78125 0.96875q-0.453125 0.34375 -1.140625 0.515625q-0.6875 0.171875 -1.6875 0.171875l-4.0 0zm1.390625 -6.078125l2.265625 0q0.921875 0 1.3125 -0.125q0.53125 -0.15625 0.796875 -0.515625q0.28125 -0.375 0.28125 -0.921875q0 -0.53125 -0.25 -0.921875q-0.25 -0.390625 -0.71875 -0.53125q-0.46875 -0.15625 -1.59375 -0.15625l-2.09375 0l0 3.171875zm0 4.84375l2.609375 0q0.671875 0 0.9375 -0.046875q0.484375 -0.09375 0.796875 -0.296875q0.328125 -0.203125 0.53125 -0.578125q0.21875 -0.390625 0.21875 -0.890625q0 -0.578125 -0.3125 -1.015625q-0.296875 -0.4375 -0.828125 -0.609375q-0.53125 -0.171875 -1.53125 -0.171875l-2.421875 0l0 3.609375zm12.503815 1.234375l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm14.24054 -1.21875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm9.291718 4.53125l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm5.21875 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm8.20929 -2.78125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm7.578125 0.328125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm11.4105835 4.53125l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm9.021759 1.234375l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.8962708 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm5.365509 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166748 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0z" fill-rule="nonzero"/><path fill="#000000" d="m304.5869 167.36261l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.2249146 7.109375l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm10.197479 3.375l0 -10.484375l3.9375 0q1.203125 0 1.921875 0.3125q0.734375 0.3125 1.140625 0.984375q0.40625 0.65625 0.40625 1.375q0 0.671875 -0.375 1.265625q-0.359375 0.59375 -1.09375 0.96875q0.953125 0.28125 1.46875 0.953125q0.515625 0.671875 0.515625 1.578125q0 0.75 -0.3125 1.390625q-0.3125 0.625 -0.78125 0.96875q-0.453125 0.34375 -1.140625 0.515625q-0.6875 0.171875 -1.6875 0.171875l-4.0 0zm1.390625 -6.078125l2.265625 0q0.921875 0 1.3125 -0.125q0.53125 -0.15625 0.796875 -0.515625q0.28125 -0.375 0.28125 -0.921875q0 -0.53125 -0.25 -0.921875q-0.25 -0.390625 -0.71875 -0.53125q-0.46875 -0.15625 -1.59375 -0.15625l-2.09375 0l0 3.171875zm0 4.84375l2.609375 0q0.671875 0 0.9375 -0.046875q0.484375 -0.09375 0.796875 -0.296875q0.328125 -0.203125 0.53125 -0.578125q0.21875 -0.390625 0.21875 -0.890625q0 -0.578125 -0.3125 -1.015625q-0.296875 -0.4375 -0.828125 -0.609375q-0.53125 -0.171875 -1.53125 -0.171875l-2.421875 0l0 3.609375zm8.431854 1.234375l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625z" fill-rule="nonzero"/><path fill="#cfe2f3" d="m28.509478 139.45203l192.28346 0l0 48.62993l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.509478 139.45203l192.28346 0l0 48.62993l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m78.9189 149.36261l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.2248993 7.109375l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm10.197479 3.375l0 -10.484375l3.9375 0q1.203125 0 1.921875 0.3125q0.734375 0.3125 1.140625 0.984375q0.40625 0.65625 0.40625 1.375q0 0.671875 -0.375 1.265625q-0.359375 0.59375 -1.09375 0.96875q0.953125 0.28125 1.46875 0.953125q0.515625 0.671875 0.515625 1.578125q0 0.75 -0.3125 1.390625q-0.3125 0.625 -0.78125 0.96875q-0.453125 0.34375 -1.140625 0.515625q-0.6875 0.171875 -1.6875 0.171875l-4.0 0zm1.390625 -6.078125l2.265625 0q0.921875 0 1.3125 -0.125q0.53125 -0.15625 0.796875 -0.515625q0.28125 -0.375 0.28125 -0.921875q0 -0.53125 -0.25 -0.921875q-0.25 -0.390625 -0.71875 -0.53125q-0.46875 -0.15625 -1.59375 -0.15625l-2.09375 0l0 3.171875zm0 4.84375l2.609375 0q0.671875 0 0.9375 -0.046875q0.484375 -0.09375 0.796875 -0.296875q0.328125 -0.203125 0.53125 -0.578125q0.21875 -0.390625 0.21875 -0.890625q0 -0.578125 -0.3125 -1.015625q-0.296875 -0.4375 -0.828125 -0.609375q-0.53125 -0.171875 -1.53125 -0.171875l-2.421875 0l0 3.609375zm12.550705 1.234375l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0zm9.896774 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm6.791733 1.53125l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.65625 1.109375l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm5.4877014 1.15625l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm9.021774 1.234375l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.8962708 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm5.3655243 0l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm10.421875 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.166733 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0z" fill-rule="nonzero"/><path fill="#000000" d="m112.30695 167.36261l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.2248993 7.109375l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm10.197479 3.375l0 -10.484375l3.9375076 0q1.203125 0 1.921875 0.3125q0.734375 0.3125 1.140625 0.984375q0.40625 0.65625 0.40625 1.375q0 0.671875 -0.375 1.265625q-0.359375 0.59375 -1.09375 0.96875q0.953125 0.28125 1.46875 0.953125q0.515625 0.671875 0.515625 1.578125q0 0.75 -0.3125 1.390625q-0.3125 0.625 -0.78125 0.96875q-0.453125 0.34375 -1.140625 0.515625q-0.6875 0.171875 -1.6875 0.171875l-4.0000076 0zm1.390625 -6.078125l2.2656326 0q0.921875 0 1.3125 -0.125q0.53125 -0.15625 0.796875 -0.515625q0.28125 -0.375 0.28125 -0.921875q0 -0.53125 -0.25 -0.921875q-0.25 -0.390625 -0.71875 -0.53125q-0.46875 -0.15625 -1.59375 -0.15625l-2.0937576 0l0 3.171875zm0 4.84375l2.6093826 0q0.671875 0 0.9375 -0.046875q0.484375 -0.09375 0.796875 -0.296875q0.328125 -0.203125 0.53125 -0.578125q0.21875 -0.390625 0.21875 -0.890625q0 -0.578125 -0.3125 -1.015625q-0.296875 -0.4375 -0.828125 -0.609375q-0.53125 -0.171875 -1.53125 -0.171875l-2.4218826 0l0 3.609375zm8.478737 1.234375l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0z" fill-rule="nonzero"/><path fill="#d9d9d9" d="m28.509478 27.969963l384.56693 0l0 48.62992l-384.56693 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.509478 27.969963l384.56693 0l0 48.62992l-384.56693 0z" fill-rule="evenodd"/><path fill="#000000" d="m184.92747 57.36492l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm7.7443542 7.21875l0 -10.5l1.171875 0l0 0.984375q0.421875 -0.578125 0.9375 -0.859375q0.515625 -0.296875 1.265625 -0.296875q0.96875 0 1.71875 0.5q0.75 0.5 1.125 1.421875q0.375 0.90625 0.375 1.984375q0 1.171875 -0.421875 2.109375q-0.40625 0.921875 -1.21875 1.421875q-0.796875 0.5 -1.671875 0.5q-0.640625 0 -1.15625 -0.265625q-0.515625 -0.28125 -0.84375 -0.6875l0 3.6875l-1.28125 0zm1.15625 -6.65625q0 1.453125 0.59375 2.15625q0.609375 0.703125 1.453125 0.703125q0.859375 0 1.46875 -0.71875q0.609375 -0.734375 0.609375 -2.25q0 -1.453125 -0.609375 -2.171875q-0.59375 -0.734375 -1.421875 -0.734375q-0.8125 0 -1.453125 0.78125q-0.640625 0.765625 -0.640625 2.234375zm6.994858 6.65625l0 -10.5l1.171875 0l0 0.984375q0.421875 -0.578125 0.9375 -0.859375q0.515625 -0.296875 1.265625 -0.296875q0.96875 0 1.71875 0.5q0.75 0.5 1.125 1.421875q0.375 0.90625 0.375 1.984375q0 1.171875 -0.421875 2.109375q-0.40625 0.921875 -1.21875 1.421875q-0.796875 0.5 -1.671875 0.5q-0.640625 0 -1.15625 -0.265625q-0.515625 -0.28125 -0.84375 -0.6875l0 3.6875l-1.28125 0zm1.15625 -6.65625q0 1.453125 0.59375 2.15625q0.609375 0.703125 1.453125 0.703125q0.859375 0 1.46875 -0.71875q0.609375 -0.734375 0.609375 -2.25q0 -1.453125 -0.609375 -2.171875q-0.59375 -0.734375 -1.421875 -0.734375q-0.8125 0 -1.453125 0.78125q-0.640625 0.765625 -0.640625 2.234375zm6.963608 3.75l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm3.2873993 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm8.209274 -2.78125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm7.328125 1.84375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm6.119858 2.65625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594757 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717743 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.307358 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0z" fill-rule="nonzero"/><path fill="#d9d2e9" d="m28.509478 195.19305l384.56693 0l0 29.606308l-384.56693 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.509478 195.19305l384.56693 0l0 29.606308l-384.56693 0z" fill-rule="evenodd"/><path fill="#000000" d="m125.783455 209.96683q0 -2.609375 1.40625 -4.078125q1.4062576 -1.484375 3.6250076 -1.484375q1.453125 0 2.609375 0.703125q1.171875 0.6875 1.78125 1.921875q0.609375 1.234375 0.609375 2.8125q0 1.59375 -0.640625 2.859375q-0.640625 1.265625 -1.828125 1.90625q-1.171875 0.640625 -2.546875 0.640625q-1.46875 0 -2.640625 -0.703125q-1.1718826 -0.71875 -1.7812576 -1.953125q-0.59375 -1.25 -0.59375 -2.625zm1.4375 0.015625q0 1.90625 1.0156326 3.0q1.015625 1.078125 2.5625 1.078125q1.5625 0 2.578125 -1.09375q1.015625 -1.109375 1.015625 -3.125q0 -1.28125 -0.4375 -2.234375q-0.4375 -0.96875 -1.265625 -1.484375q-0.828125 -0.53125 -1.875 -0.53125q-1.46875 0 -2.53125 1.015625q-1.0625076 1.015625 -1.0625076 3.375zm9.915733 1.71875l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm12.366455 3.375l4.015625 -10.484375l1.5 0l4.2968597 10.484375l-1.578125 0l-1.2343597 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm8.931839 4.3125l-1.203125 0l0 -10.484375l1.296875 0l0 3.734375q0.8125 -1.015625 2.078125 -1.015625q0.703125 0 1.328125 0.28125q0.625 0.28125 1.03125 0.796875q0.40625 0.5 0.625 1.234375q0.234375 0.71875 0.234375 1.53125q0 1.96875 -0.96875 3.03125q-0.953125 1.0625 -2.3125 1.0625q-1.34375 0 -2.109375 -1.125l0 0.953125zm-0.015625 -3.859375q0 1.375 0.375 1.984375q0.609375 0.984375 1.640625 0.984375q0.84375 0 1.453125 -0.734375q0.625 -0.734375 0.625 -2.1875q0 -1.484375 -0.59375 -2.1875q-0.59375 -0.71875 -1.421875 -0.71875q-0.84375 0 -1.46875 0.734375q-0.609375 0.734375 -0.609375 2.125zm6.463623 1.59375l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.65625 1.109375l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2438354 1.15625l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm9.849396 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm8.260498 1.03125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm5.1875 1.625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm1.2594604 -7.859375l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm2.7717896 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.3073425 3.796875l0 -7.59375l1.15625 0l0 1.078125q0.84375 -1.25 2.421875 -1.25q0.6875 0 1.265625 0.25q0.578125 0.234375 0.859375 0.640625q0.28125 0.40625 0.40625 0.953125q0.0625 0.359375 0.0625 1.25l0 4.671875l-1.28125 0l0 -4.625q0 -0.78125 -0.15625 -1.171875q-0.15625 -0.390625 -0.546875 -0.625q-0.375 -0.234375 -0.890625 -0.234375q-0.8125 0 -1.421875 0.53125q-0.59375 0.515625 -0.59375 1.96875l0 4.15625l-1.28125 0zm12.3324585 0l0 -10.484375l1.390625 0l0 9.25l5.15625 0l0 1.234375l-6.546875 0zm12.994873 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm3.2448425 6.734375l-0.140625 -1.203125q0.421875 0.109375 0.734375 0.109375q0.4375 0 0.6875 -0.140625q0.265625 -0.140625 0.421875 -0.40625q0.125 -0.1875 0.390625 -0.953125q0.046875 -0.109375 0.125 -0.3125l-2.890625 -7.609375l1.390625 0l1.578125 4.390625q0.3125 0.84375 0.546875 1.765625q0.234375 -0.890625 0.53125 -1.734375l1.625 -4.421875l1.296875 0l-2.890625 7.71875q-0.46875 1.25 -0.734375 1.734375q-0.34375 0.625 -0.78125 0.921875q-0.4375 0.296875 -1.0625 0.296875q-0.375 0 -0.828125 -0.15625zm12.593765 -5.375l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.1718903 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.5156403 0 2.4843903 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.6562653 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.50001526 -0.375 0.79689026 -1.203125zm-4.2343903 -2.078125l4.2500153 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.62501526 -0.75 -1.6093903 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm7.1667633 4.53125l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm11.421356 3.078125q-1.0625 -1.34375 -1.796875 -3.140625q-0.734375 -1.8125 -0.734375 -3.734375q0 -1.703125 0.546875 -3.265625q0.640625 -1.8125 1.984375 -3.609375l0.921875 0q-0.859375 1.484375 -1.140625 2.125q-0.4375 0.984375 -0.6875 2.0625q-0.296875 1.34375 -0.296875 2.6875q0 3.453125 2.125 6.875l-0.921875 0zm2.1618958 -8.1875q0 -2.609375 1.40625 -4.078125q1.40625 -1.484375 3.625 -1.484375q1.453125 0 2.609375 0.703125q1.171875 0.6875 1.78125 1.921875q0.609375 1.234375 0.609375 2.8125q0 1.59375 -0.640625 2.859375q-0.640625 1.265625 -1.828125 1.90625q-1.171875 0.640625 -2.546875 0.640625q-1.46875 0 -2.640625 -0.703125q-1.171875 -0.71875 -1.78125 -1.953125q-0.59375 -1.25 -0.59375 -2.625zm1.4375 0.015625q0 1.90625 1.015625 3.0q1.015625 1.078125 2.5625 1.078125q1.5625 0 2.578125 -1.09375q1.015625 -1.109375 1.015625 -3.125q0 -1.28125 -0.4375 -2.234375q-0.4375 -0.96875 -1.265625 -1.484375q-0.828125 -0.53125 -1.875 -0.53125q-1.46875 0 -2.53125 1.015625q-1.0625 1.015625 -1.0625 3.375zm9.915741 1.71875l1.3125 -0.109375q0.09375 0.78125 0.421875 1.296875q0.34375 0.5 1.0625 0.8125q0.71875 0.3125 1.609375 0.3125q0.796875 0 1.40625 -0.234375q0.609375 -0.234375 0.90625 -0.640625q0.296875 -0.421875 0.296875 -0.90625q0 -0.5 -0.296875 -0.859375q-0.28125 -0.375 -0.9375 -0.625q-0.421875 -0.171875 -1.875 -0.515625q-1.4375 -0.34375 -2.015625 -0.65625q-0.75 -0.390625 -1.125 -0.96875q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.796875 0.4375 -1.484375q0.453125 -0.6875 1.3125 -1.046875q0.875 -0.359375 1.9375 -0.359375q1.171875 0 2.0625 0.375q0.890625 0.375 1.359375 1.109375q0.484375 0.734375 0.515625 1.65625l-1.328125 0.09375q-0.109375 -1.0 -0.734375 -1.5q-0.609375 -0.515625 -1.8125 -0.515625q-1.265625 0 -1.84375 0.46875q-0.578125 0.46875 -0.578125 1.109375q0 0.5625 0.421875 0.9375q0.390625 0.359375 2.078125 0.75q1.703125 0.375 2.328125 0.65625q0.921875 0.421875 1.359375 1.078125q0.4375 0.640625 0.4375 1.484375q0 0.84375 -0.484375 1.59375q-0.484375 0.734375 -1.390625 1.140625q-0.890625 0.40625 -2.015625 0.40625q-1.421875 0 -2.390625 -0.40625q-0.953125 -0.421875 -1.5 -1.25q-0.546875 -0.828125 -0.578125 -1.890625zm9.103729 3.375l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm7.8537292 4.3125l0 -10.484375l1.390625 0l0 9.25l5.15625 0l0 1.234375l-6.546875 0zm8.885468 3.078125l-0.921875 0q2.140625 -3.421875 2.140625 -6.875q0 -1.34375 -0.3125 -2.671875q-0.25 -1.0625 -0.671875 -2.046875q-0.28125 -0.65625 -1.15625 -2.15625l0.921875 0q1.34375 1.796875 1.984375 3.609375q0.546875 1.5625 0.546875 3.265625q0 1.921875 -0.734375 3.734375q-0.734375 1.796875 -1.796875 3.140625z" fill-rule="nonzero"/><path fill="#cfe2f3" d="m28.509478 83.71099l192.28346 0l0 48.62992l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m28.509478 83.71099l192.28346 0l0 48.62992l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m76.95251 104.10596l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0zm9.896774 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm6.791733 1.53125l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.65625 1.109375l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm12.972076 -2.515625l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.9123993 3.671875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.240532 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.6562576 0.25 -1.4218826 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625076 -0.1875 2.2968826 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.4531326 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.7187576 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.0937576 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.7343826 0 1.3125076 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm2.7917328 1.546875l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm7.328125 0l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.122101 2.265625l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm7.9006042 4.3125l0 -10.484375l3.96875 0q1.046875 0 1.59375 0.09375q0.765625 0.125 1.28125 0.484375q0.53125 0.359375 0.84375 1.015625q0.328125 0.65625 0.328125 1.4375q0 1.328125 -0.859375 2.265625q-0.84375 0.921875 -3.078125 0.921875l-2.6875 0l0 4.265625l-1.390625 0zm1.390625 -5.5l2.71875 0q1.34375 0 1.90625 -0.5q0.5625 -0.5 0.5625 -1.40625q0 -0.671875 -0.328125 -1.140625q-0.328125 -0.46875 -0.875 -0.609375q-0.359375 -0.09375 -1.296875 -0.09375l-2.6875 0l0 3.75zm8.619354 5.5l0 -10.484375l1.390625 0l0 10.484375l-1.390625 0z" fill-rule="nonzero"/><path fill="#000000" d="m113.3875 122.10595l0 -9.25l-3.453125 0l0 -1.234375l8.3125 0l0 1.234375l-3.46875 0l0 9.25l-1.390625 0zm13.171371 -10.484375l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.7405243 10.484375l0 -10.484375l1.390625 0l0 4.296875l5.453125 0l0 -4.296875l1.390625 0l0 10.484375l-1.390625 0l0 -4.9375l-5.453125 0l0 4.9375l-1.390625 0z" fill-rule="nonzero"/><path fill="#d9ead3" d="m220.78944 83.71099l192.28346 0l0 48.62992l-192.28346 0z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m220.78944 83.71099l192.28346 0l0 48.62992l-192.28346 0z" fill-rule="evenodd"/><path fill="#000000" d="m261.85388 104.10596l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625zm14.24054 -1.21875l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm9.291718 4.53125l-2.890625 -7.59375l1.359375 0l1.625 4.546875q0.265625 0.734375 0.5 1.53125q0.15625 -0.609375 0.46875 -1.453125l1.6875 -4.625l1.328125 0l-2.875 7.59375l-1.203125 0zm5.21875 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm8.20929 -2.78125l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm7.578125 0.328125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm18.894958 0.859375l1.390625 0.34375q-0.4375 1.703125 -1.578125 2.609375q-1.125 0.890625 -2.765625 0.890625q-1.6875 0 -2.75 -0.6875q-1.0625 -0.6875 -1.625 -2.0q-0.546875 -1.3125 -0.546875 -2.8125q0 -1.640625 0.625 -2.859375q0.625 -1.21875 1.78125 -1.84375q1.15625 -0.640625 2.546875 -0.640625q1.5625 0 2.640625 0.8125q1.078125 0.796875 1.5 2.25l-1.375 0.3125q-0.359375 -1.140625 -1.0625 -1.65625q-0.6875 -0.53125 -1.734375 -0.53125q-1.21875 0 -2.03125 0.578125q-0.8125 0.578125 -1.140625 1.5625q-0.328125 0.96875 -0.328125 2.015625q0 1.328125 0.390625 2.328125q0.390625 1.0 1.21875 1.5q0.828125 0.484375 1.78125 0.484375q1.171875 0 1.96875 -0.671875q0.8125 -0.671875 1.09375 -1.984375zm2.912384 3.671875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.24054 -0.9375q-0.71875 0.609375 -1.375 0.859375q-0.65625 0.25 -1.421875 0.25q-1.25 0 -1.921875 -0.609375q-0.671875 -0.609375 -0.671875 -1.5625q0 -0.5625 0.25 -1.015625q0.25 -0.46875 0.65625 -0.75q0.421875 -0.28125 0.9375 -0.421875q0.375 -0.09375 1.140625 -0.1875q1.5625 -0.1875 2.296875 -0.453125q0.015625 -0.265625 0.015625 -0.328125q0 -0.796875 -0.375 -1.109375q-0.484375 -0.4375 -1.453125 -0.4375q-0.921875 0 -1.359375 0.328125q-0.421875 0.3125 -0.625 1.109375l-1.265625 -0.171875q0.171875 -0.796875 0.5625 -1.296875q0.390625 -0.5 1.140625 -0.765625q0.75 -0.265625 1.71875 -0.265625q0.984375 0 1.59375 0.234375q0.609375 0.21875 0.890625 0.5625q0.28125 0.34375 0.40625 0.875q0.0625 0.328125 0.0625 1.1875l0 1.71875q0 1.796875 0.078125 2.28125q0.078125 0.46875 0.328125 0.90625l-1.34375 0q-0.203125 -0.40625 -0.265625 -0.9375zm-0.109375 -2.875q-0.703125 0.28125 -2.09375 0.484375q-0.796875 0.109375 -1.125 0.265625q-0.328125 0.140625 -0.515625 0.421875q-0.171875 0.265625 -0.171875 0.59375q0 0.515625 0.390625 0.859375q0.390625 0.34375 1.140625 0.34375q0.734375 0 1.3125 -0.3125q0.59375 -0.328125 0.859375 -0.890625q0.203125 -0.4375 0.203125 -1.296875l0 -0.46875zm2.7917175 1.546875l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm7.328125 0l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125zm10.122101 2.265625l4.015625 -10.484375l1.5 0l4.296875 10.484375l-1.578125 0l-1.234375 -3.171875l-4.375 0l-1.15625 3.171875l-1.46875 0zm3.015625 -4.3125l3.5625 0l-1.09375 -2.90625q-0.5 -1.3125 -0.75 -2.171875q-0.203125 1.015625 -0.5625 2.0l-1.15625 3.078125zm7.9006042 4.3125l0 -10.484375l3.96875 0q1.046875 0 1.59375 0.09375q0.765625 0.125 1.28125 0.484375q0.53125 0.359375 0.84375 1.015625q0.328125 0.65625 0.328125 1.4375q0 1.328125 -0.859375 2.265625q-0.84375 0.921875 -3.078125 0.921875l-2.6875 0l0 4.265625l-1.390625 0zm1.390625 -5.5l2.71875 0q1.34375 0 1.90625 -0.5q0.5625 -0.5 0.5625 -1.40625q0 -0.671875 -0.328125 -1.140625q-0.328125 -0.46875 -0.875 -0.609375q-0.359375 -0.09375 -1.296875 -0.09375l-2.6875 0l0 3.75zm8.619354 5.5l0 -10.484375l1.390625 0l0 10.484375l-1.390625 0z" fill-rule="nonzero"/><path fill="#000000" d="m305.66745 122.10595l0 -9.25l-3.453125 0l0 -1.234375l8.3125 0l0 1.234375l-3.46875 0l0 9.25l-1.390625 0zm13.171387 -10.484375l1.390625 0l0 6.046875q0 1.59375 -0.359375 2.53125q-0.359375 0.921875 -1.296875 1.5q-0.9375 0.578125 -2.453125 0.578125q-1.46875 0 -2.40625 -0.5q-0.9375 -0.515625 -1.34375 -1.46875q-0.390625 -0.96875 -0.390625 -2.640625l0 -6.046875l1.390625 0l0 6.046875q0 1.375 0.25 2.015625q0.25 0.640625 0.859375 1.0q0.625 0.34375 1.53125 0.34375q1.53125 0 2.171875 -0.6875q0.65625 -0.703125 0.65625 -2.671875l0 -6.046875zm3.693634 10.484375l0 -10.484375l3.625 0q1.21875 0 1.859375 0.140625q0.90625 0.203125 1.546875 0.75q0.828125 0.703125 1.234375 1.796875q0.40625 1.09375 0.40625 2.5q0 1.1875 -0.28125 2.109375q-0.265625 0.921875 -0.703125 1.53125q-0.4375 0.609375 -0.96875 0.953125q-0.515625 0.34375 -1.25 0.53125q-0.71875 0.171875 -1.671875 0.171875l-3.796875 0zm1.390625 -1.234375l2.25 0q1.03125 0 1.625 -0.1875q0.59375 -0.203125 0.9375 -0.546875q0.5 -0.5 0.765625 -1.328125q0.28125 -0.84375 0.28125 -2.03125q0 -1.640625 -0.546875 -2.515625q-0.53125 -0.890625 -1.3125 -1.1875q-0.546875 -0.21875 -1.796875 -0.21875l-2.203125 0l0 8.015625z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m414.7422 230.80695l0 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m414.7422 230.80695l0 0" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m416.97958 84.35958l69.102356 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="8.0,3.0" d="m416.97958 84.35958l69.102356 0" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m430.57208 84.35958l69.10239 0l0 37.5748l-69.10239 0z" fill-rule="evenodd"/><path fill="#000000" d="m441.68146 103.87958l0 -7.578125l-2.828125 0l0 -1.015625l6.8125 0l0 1.015625l-2.84375 0l0 7.578125l-1.140625 0zm4.5722656 -7.375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6660156 0l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.626953 2.390625l-0.125 -0.984375q0.34375 0.09375 0.609375 0.09375q0.34375 0 0.546875 -0.125q0.21875 -0.109375 0.359375 -0.3125q0.09375 -0.171875 0.328125 -0.796875q0.015625 -0.078125 0.09375 -0.25l-2.375 -6.234375l1.140625 0l1.296875 3.59375q0.25 0.6875 0.453125 1.453125q0.1875 -0.734375 0.4375 -1.421875l1.328125 -3.625l1.046875 0l-2.359375 6.328125q-0.390625 1.015625 -0.59375 1.40625q-0.28125 0.53125 -0.65625 0.765625q-0.359375 0.25 -0.859375 0.25q-0.296875 0 -0.671875 -0.140625zm11.8125 -10.984375l1.140625 0l0 4.96875q0 1.296875 -0.296875 2.0625q-0.296875 0.75 -1.0625 1.234375q-0.765625 0.46875 -2.0 0.46875q-1.203125 0 -1.984375 -0.40625q-0.765625 -0.421875 -1.09375 -1.203125q-0.328125 -0.796875 -0.328125 -2.15625l0 -4.96875l1.140625 0l0 4.953125q0 1.125 0.203125 1.65625q0.21875 0.53125 0.71875 0.828125q0.515625 0.28125 1.25 0.28125q1.25 0 1.78125 -0.5625q0.53125 -0.578125 0.53125 -2.203125l0 -4.953125zm2.6503906 5.828125l1.0625 -0.09375q0.078125 0.65625 0.359375 1.0625q0.28125 0.40625 0.859375 0.671875q0.59375 0.25 1.328125 0.25q0.640625 0 1.140625 -0.1875q0.5 -0.203125 0.734375 -0.53125q0.25 -0.34375 0.25 -0.734375q0 -0.40625 -0.234375 -0.703125q-0.234375 -0.3125 -0.765625 -0.515625q-0.359375 -0.140625 -1.546875 -0.421875q-1.171875 -0.28125 -1.640625 -0.53125q-0.625 -0.328125 -0.921875 -0.796875q-0.296875 -0.484375 -0.296875 -1.078125q0 -0.640625 0.359375 -1.203125q0.375 -0.578125 1.078125 -0.859375q0.71875 -0.296875 1.578125 -0.296875q0.953125 0 1.6875 0.3125q0.734375 0.296875 1.125 0.90625q0.390625 0.59375 0.421875 1.34375l-1.09375 0.078125q-0.09375 -0.8125 -0.609375 -1.21875q-0.5 -0.421875 -1.484375 -0.421875q-1.03125 0 -1.5 0.375q-0.46875 0.375 -0.46875 0.90625q0 0.46875 0.328125 0.765625q0.328125 0.296875 1.703125 0.609375q1.390625 0.3125 1.90625 0.546875q0.75 0.359375 1.109375 0.890625q0.359375 0.515625 0.359375 1.21875q0 0.6875 -0.390625 1.296875q-0.390625 0.59375 -1.125 0.9375q-0.734375 0.328125 -1.65625 0.328125q-1.171875 0 -1.96875 -0.328125q-0.78125 -0.34375 -1.234375 -1.03125q-0.4375 -0.6875 -0.453125 -1.546875zm8.332031 2.765625l0 -8.59375l3.234375 0q0.984375 0 1.578125 0.265625q0.59375 0.265625 0.921875 0.8125q0.34375 0.53125 0.34375 1.125q0 0.546875 -0.296875 1.03125q-0.296875 0.484375 -0.90625 0.78125q0.78125 0.234375 1.1875 0.796875q0.421875 0.546875 0.421875 1.296875q0 0.59375 -0.25 1.125q-0.25 0.515625 -0.625 0.796875q-0.375 0.28125 -0.953125 0.421875q-0.5625 0.140625 -1.375 0.140625l-3.28125 0zm1.140625 -4.984375l1.859375 0q0.75 0 1.078125 -0.09375q0.4375 -0.125 0.65625 -0.421875q0.21875 -0.3125 0.21875 -0.765625q0 -0.421875 -0.203125 -0.75q-0.203125 -0.328125 -0.59375 -0.4375q-0.375 -0.125 -1.296875 -0.125l-1.71875 0l0 2.59375zm0 3.96875l2.140625 0q0.546875 0 0.765625 -0.046875q0.40625 -0.0625 0.65625 -0.21875q0.265625 -0.171875 0.4375 -0.484375q0.171875 -0.3125 0.171875 -0.71875q0 -0.484375 -0.25 -0.84375q-0.234375 -0.359375 -0.671875 -0.5q-0.4375 -0.140625 -1.265625 -0.140625l-1.984375 0l0 2.953125z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m416.97958 231.92447l69.102356 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" stroke-dasharray="8.0,3.0" d="m416.97958 231.92447l69.102356 0" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m429.0601 231.92447l72.12598 0l0 37.5748l-72.12598 0z" fill-rule="evenodd"/><path fill="#000000" d="m437.9507 251.44447l0 -8.59375l1.71875 0l2.03125 6.078125q0.28125 0.859375 0.40625 1.28125q0.140625 -0.46875 0.453125 -1.375l2.0625 -5.984375l1.53125 0l0 8.59375l-1.09375 0l0 -7.1875l-2.5 7.1875l-1.03125 0l-2.484375 -7.3125l0 7.3125l-1.09375 0zm16.167969 -3.015625l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm8.166016 -5.578125l1.140625 0l0 4.96875q0 1.296875 -0.296875 2.0625q-0.296875 0.75 -1.0625 1.234375q-0.765625 0.46875 -2.0 0.46875q-1.203125 0 -1.984375 -0.40625q-0.765625 -0.421875 -1.09375 -1.203125q-0.328125 -0.796875 -0.328125 -2.15625l0 -4.96875l1.140625 0l0 4.953125q0 1.125 0.203125 1.65625q0.21875 0.53125 0.71875 0.828125q0.515625 0.28125 1.25 0.28125q1.25 0 1.78125 -0.5625q0.53125 -0.578125 0.53125 -2.203125l0 -4.953125zm6.359375 8.59375l0 -8.59375l3.25 0q0.84375 0 1.296875 0.078125q0.640625 0.109375 1.0625 0.40625q0.4375 0.296875 0.6875 0.828125q0.265625 0.53125 0.265625 1.171875q0 1.09375 -0.703125 1.859375q-0.6875 0.75 -2.515625 0.75l-2.203125 0l0 3.5l-1.140625 0zm1.140625 -4.5l2.21875 0q1.109375 0 1.5625 -0.40625q0.46875 -0.421875 0.46875 -1.171875q0 -0.53125 -0.28125 -0.90625q-0.265625 -0.390625 -0.703125 -0.515625q-0.296875 -0.078125 -1.0625 -0.078125l-2.203125 0l0 3.078125zm6.3476562 1.390625q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625 -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm5.970703 3.109375l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm6.3085938 -0.9375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125z" fill-rule="nonzero"/></g></svg>
diff --git a/docs/conf.py b/docs/conf.py
index 9e9784fb7..86ddcf672 100755
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -53,6 +53,25 @@ def preprocess_readme():
tgt = Path(__file__).parent.parent / "README_processed.rst"
if src.exists():
content = src.read_text(encoding='utf-8')
+ # if the matching is inside a table, keep the table cell width by adding the same number of spaces in the end of the line
+ # match pattern: | ... `... <docs/...>`_ ... |
+ # change into: | ... `... <...>`_ ... |
+ def _rewrite_table_line(line):
+ if not (line.startswith('|') and line.rstrip().endswith('|')):
+ return line
+
+ rewritten = re.sub(r"<docs/([^>]+)>", r"<\1>", line)
+ delta = len(line) - len(rewritten) - 1 # -1 for rst->html
+
+ if delta > 0:
+ last_pipe = rewritten.rfind('|')
+ if last_pipe >= 0:
+ rewritten = rewritten[:last_pipe] + (' ' * delta) + rewritten[last_pipe:]
+
+ return rewritten
+
+ content = ''.join(_rewrite_table_line(line) for line in content.splitlines(keepends=True))
+
content = re.sub(r"docs/", r"", content)
content = re.sub(r"\.rst\b", r".html", content)
if not content.endswith("\n"):
diff --git a/docs/reference/device_issues.rst b/docs/reference/device_issues.rst
new file mode 100644
index 000000000..ae9cd55f1
--- /dev/null
+++ b/docs/reference/device_issues.rst
@@ -0,0 +1,35 @@
+Device specific known issues and workarounds
+===============================================
+This page lists known issues and workarounds for specific devices.
+
+NXP LPC54600
+----------------
+**Severity: High**
+
+**Not recommended for USB device applications (except high-speed host controller)**
+
+Reference: `LPC54600 Errata Sheet`_
+
+.. _LPC54600 Errata Sheet: https://www.nxp.com/docs/en/errata/ES_LPC546XX.pdf
+
+The LPC54600 series have a very buggy USB controller, with 17 issues listed in the errata which is more than half of the total issues.
+
+Most severe issues are:
+
+- USB.2: In USB high-speed device mode, the NBytes field is not correct after BULK IN transfer
+- USB.5: In USB full-speed host mode, linked list on done queue is broken.
+- USB.15: USB high-speed device in endpoint TX data corruption
+
+WCH CH32F20x/CH32V20x/CH32V30x
+---------------------------------
+**Severity: Medium**
+
+**Not recommended for USB audio applications**
+
+Reference: `CH32V30X Reference Manual`_ USBFS/USBHS controller chapter
+
+.. _CH32V30X Reference Manual: https://www.wch-ic.com/downloads/CH32FV2x_V3xRM_PDF.html
+
+Data corruption may occur on isochronous endpoints. Due to the lacking of FIFO for interrupt status registers, later completed transfer will overwrite `INT_ST` and `RX_LEN` register if previous transfer processing is not completed.
+
+Other types of transfers are not affected.
diff --git a/docs/reference/index.rst b/docs/reference/index.rst
index 148e8a63b..c66ce618f 100644
--- a/docs/reference/index.rst
+++ b/docs/reference/index.rst
@@ -13,4 +13,5 @@ Complete reference documentation for TinyUSB APIs, configuration, and supported
boards
dependencies
concurrency
+ device_issues
glossary
diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst
index bb9f15166..613c7fcef 100644
--- a/docs/troubleshooting.rst
+++ b/docs/troubleshooting.rst
@@ -76,6 +76,8 @@ Invalid board name in build command.
Runtime Issues
==============
+Check :doc:`reference/device_issues` for known hardware-specific issues that may affect USB functionality on your device.
+
Device Mode Problems
--------------------
diff --git a/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt b/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt
index d43a72e58..66ef19fbc 100644
--- a/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt
+++ b/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt
@@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+# Need to set Espressif defaults before project() is called
+if(FAMILY STREQUAL "espressif")
+ list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
+endif()
+
project(audio_4_channel_mic_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
diff --git a/examples/device/audio_test_freertos/CMakeLists.txt b/examples/device/audio_test_freertos/CMakeLists.txt
index 71d65eccc..a39e56822 100644
--- a/examples/device/audio_test_freertos/CMakeLists.txt
+++ b/examples/device/audio_test_freertos/CMakeLists.txt
@@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+# Need to set Espressif defaults before project() is called
+if(FAMILY STREQUAL "espressif")
+ list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
+endif()
+
project(audio_test_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
diff --git a/examples/device/board_test/CMakeLists.txt b/examples/device/board_test/CMakeLists.txt
index bd7b8e0ca..f14d72c08 100644
--- a/examples/device/board_test/CMakeLists.txt
+++ b/examples/device/board_test/CMakeLists.txt
@@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+# Need to set Espressif defaults before project() is called
+if(FAMILY STREQUAL "espressif")
+ list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
+endif()
+
project(board_test C CXX ASM)
# Checks this example is valid for the family and initializes the project
diff --git a/examples/device/board_test/src/main.c b/examples/device/board_test/src/main.c
index 71e7e1da7..96dc1bd30 100644
--- a/examples/device/board_test/src/main.c
+++ b/examples/device/board_test/src/main.c
@@ -54,6 +54,19 @@ void tusb_time_delay_ms_api(uint32_t ms) {
//
//--------------------------------------------------------------------+
+// CI_BUILD (defined for all CI builds, see hw/bsp/family_support.cmake) skips the
+// blink/echo loop below: after HIL tests, this firmware is flashed to park the
+// board in a quiet, low-power idle state (no USB, LED, or UART activity).
+#ifdef CI_BUILD
+int main(void) {
+ while (1) {
+ #if defined(ESP_PLATFORM)
+ vTaskDelay(portMAX_DELAY);
+ #endif
+ }
+}
+
+#else
// Task parameter type: ULONG for ThreadX, void* for FreeRTOS and noos
#if CFG_TUSB_OS == OPT_OS_THREADX
#define RTOS_PARAM ULONG
@@ -122,12 +135,6 @@ int main(void) {
return 0;
}
-#ifdef ESP_PLATFORM
-void app_main(void) {
- main();
-}
-#endif
-
//--------------------------------------------------------------------+
// FreeRTOS
//--------------------------------------------------------------------+
@@ -173,3 +180,10 @@ void tx_application_define(void *first_unused_memory) {
1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
}
#endif
+#endif // CI_BUILD
+
+#ifdef ESP_PLATFORM
+void app_main(void) {
+ main();
+}
+#endif
diff --git a/examples/device/cdc_msc_freertos/CMakeLists.txt b/examples/device/cdc_msc_freertos/CMakeLists.txt
index 429000427..1eafd529a 100644
--- a/examples/device/cdc_msc_freertos/CMakeLists.txt
+++ b/examples/device/cdc_msc_freertos/CMakeLists.txt
@@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+# Need to set Espressif defaults before project() is called
+if(FAMILY STREQUAL "espressif")
+ list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
+endif()
+
project(cdc_msc_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c
index f2f71d089..4a920c90b 100644
--- a/examples/device/cdc_msc_freertos/src/main.c
+++ b/examples/device/cdc_msc_freertos/src/main.c
@@ -88,7 +88,7 @@ int main(void) {
#else
xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
- xTaskCreate(cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL);
+ xTaskCreate(cdc_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
#endif
#ifndef ESP_PLATFORM
diff --git a/examples/device/hid_composite_freertos/CMakeLists.txt b/examples/device/hid_composite_freertos/CMakeLists.txt
index b52373011..2081a7782 100644
--- a/examples/device/hid_composite_freertos/CMakeLists.txt
+++ b/examples/device/hid_composite_freertos/CMakeLists.txt
@@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+# Need to set Espressif defaults before project() is called
+if(FAMILY STREQUAL "espressif")
+ list(APPEND SDKCONFIG_DEFAULTS "${CMAKE_CURRENT_LIST_DIR}/sdkconfig.defaults")
+endif()
+
project(hid_composite_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
diff --git a/examples/device/midi2_device/CMakeLists.txt b/examples/device/midi2_device/CMakeLists.txt
index 295af6550..f1fe09db2 100644
--- a/examples/device/midi2_device/CMakeLists.txt
+++ b/examples/device/midi2_device/CMakeLists.txt
@@ -30,4 +30,6 @@ target_include_directories(${PROJECT_NAME} PUBLIC
family_configure_device_example(${PROJECT_NAME} noos)
# Suppress pre-existing warning in usbd.c (uint8_t comparison always true/false)
-target_compile_options(${PROJECT_NAME} PRIVATE -Wno-type-limits)
+if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ target_compile_options(${PROJECT_NAME} PRIVATE -Wno-type-limits)
+endif()
diff --git a/examples/device/midi2_device/src/main.c b/examples/device/midi2_device/src/main.c
index 62741ac41..ce052a20b 100644
--- a/examples/device/midi2_device/src/main.c
+++ b/examples/device/midi2_device/src/main.c
@@ -715,6 +715,4 @@ int main(void) {
}
}
}
-
- return 0;
}
diff --git a/examples/device/msc_dual_lun/src/main.c b/examples/device/msc_dual_lun/src/main.c
index a4ade6f9b..1d764f12c 100644
--- a/examples/device/msc_dual_lun/src/main.c
+++ b/examples/device/msc_dual_lun/src/main.c
@@ -71,7 +71,7 @@ static void usb_device_init(void) {
board_init_after_tusb();
}
-#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO
+#if CFG_TUSB_OS_HAS_SCHEDULER
static void usb_device_task(RTOS_PARAM param) {
(void) param;
usb_device_init();
diff --git a/examples/dual/dynamic_switch/src/main.c b/examples/dual/dynamic_switch/src/main.c
index c9ad2a835..f67cd885c 100644
--- a/examples/dual/dynamic_switch/src/main.c
+++ b/examples/dual/dynamic_switch/src/main.c
@@ -79,6 +79,9 @@ StaticTask_t usb_taskdef;
StackType_t cdc_stack[CDC_STACK_SIZE];
StaticTask_t cdc_taskdef;
+
+StackType_t devinfo_stack[USBH_STACK_SIZE];
+StaticTask_t devinfo_taskdef;
#endif
#endif
@@ -87,14 +90,13 @@ static tusb_role_t current_role = TUSB_ROLE_DEVICE;
#if CFG_TUSB_OS == OPT_OS_FREERTOS
static void usb_task(void *param);
-void led_blinking_task(void *param);
-void cdc_task(void *params);
-#else
-void led_blinking_task(void);
-void cdc_task(void);
#endif
+void led_blinking_task(void *param);
+void cdc_task(void *param);
+void print_devinfo_task(void *param);
+
void usb_mode_switch(void);
-static void print_device_info(uint8_t daddr);
+static void print_one_device(uint8_t daddr);
static void print_utf16(uint16_t* temp_buf, size_t buf_len);
// Declare buffer for USB transfer
@@ -124,11 +126,13 @@ int main(void) {
xTaskCreateStatic(usb_task, "usb", USBD_STACK_SIZE > USBH_STACK_SIZE ? USBD_STACK_SIZE : USBH_STACK_SIZE,
NULL, configMAX_PRIORITIES-1, usb_stack, &usb_taskdef);
xTaskCreateStatic(cdc_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, cdc_stack, &cdc_taskdef);
+ xTaskCreateStatic(print_devinfo_task, "devinfo", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, devinfo_stack, &devinfo_taskdef);
#else
xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(usb_task, "usb", USBD_STACK_SIZE > USBH_STACK_SIZE ? USBD_STACK_SIZE : USBH_STACK_SIZE,
NULL, configMAX_PRIORITIES - 1, NULL);
xTaskCreate(cdc_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
+ xTaskCreate(print_devinfo_task, "devinfo", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
#endif
#ifndef ESP_PLATFORM
@@ -162,12 +166,13 @@ int main(void) {
// Process USB tasks based on current mode
if (current_role == TUSB_ROLE_DEVICE) {
tud_task();
- cdc_task();
+ cdc_task(NULL);
} else {
tuh_task();
+ print_devinfo_task(NULL);
}
- led_blinking_task();
+ led_blinking_task(NULL);
}
#endif
}
@@ -227,21 +232,28 @@ static void usb_task(void *param) {
void usb_mode_switch(void) {
printf("\r\n--- Switching USB mode ---\r\n");
- // Deinitialize current mode
- if (current_role == TUSB_ROLE_DEVICE) {
+ // Snapshot then clear current_role BEFORE tusb_deinit() so concurrent
+ // tasks (cdc_task / print_devinfo_task on RTOS) see the role-change
+ // boundary and exit cleanly instead of calling host/device APIs against
+ // a deinitialised stack.
+ const tusb_role_t prev_role = current_role;
+ current_role = TUSB_ROLE_INVALID;
+
+ if (prev_role == TUSB_ROLE_DEVICE) {
printf("Stopping DEVICE mode...\r\n");
- tusb_deinit(BOARD_RHPORT);
} else {
printf("Stopping HOST mode...\r\n");
- tusb_deinit(BOARD_RHPORT);
}
+ tusb_deinit(BOARD_RHPORT);
#if CFG_TUSB_OS == OPT_OS_FREERTOS
vTaskDelay(pdMS_TO_TICKS(100)); // Small delay for clean transition
#else
- tusb_time_delay_ms_api(100); // Small delay for clean transition
-#endif // Switch to the other mode
- if (current_role == TUSB_ROLE_DEVICE) {
+ tusb_time_delay_ms_api(100);
+#endif
+
+ // Switch to the other mode
+ if (prev_role == TUSB_ROLE_DEVICE) {
printf("Starting HOST mode...\r\n");
tusb_rhport_init_t host_init = {
.role = TUSB_ROLE_HOST,
@@ -267,61 +279,29 @@ void usb_mode_switch(void) {
// Device Mode: CDC Task
//--------------------------------------------------------------------+
+void cdc_task(void *param) {
+ (void) param;
#if CFG_TUSB_OS == OPT_OS_FREERTOS
-void cdc_task(void *params) {
- (void) params;
-
- // RTOS forever loop
while (1) {
- // Only process CDC when in device mode
+#endif
+ // Only touch device-CDC APIs while we're in device mode. After
+ // usb_mode_switch() sets current_role to INVALID and tusb_deinit() runs,
+ // calling tud_cdc_write_flush() here would hit a deinit'd device stack.
if (current_role == TUSB_ROLE_DEVICE) {
- // Connected and there are data available
- while (tud_cdc_available()) {
+ if (tud_cdc_available()) {
uint8_t buf[64];
-
- // Read data
- uint32_t count = tud_cdc_read(buf, sizeof(buf));
-
- // Echo back
- tud_cdc_write(buf, count);
-
- // Add newline for carriage return
- for (uint32_t i = 0; i < count; i++) {
- if (buf[i] == '\r') {
- tud_cdc_write_char('\n');
- break;
- }
+ const uint32_t count = tud_cdc_read(buf, sizeof(buf));
+ if (count) {
+ tud_cdc_write(buf, count);
}
}
-
tud_cdc_write_flush();
}
-
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
vTaskDelay(pdMS_TO_TICKS(10));
}
-}
-#else
-void cdc_task(void) {
- // Connected and there are data available
- if (tud_cdc_available()) {
- uint8_t buf[64];
-
- // Read data
- uint32_t count = tud_cdc_read(buf, sizeof(buf));
-
- // Echo back
- for (uint32_t i = 0; i < count; i++) {
- tud_cdc_write_char(buf[i]);
-
- if (buf[i] == '\r') {
- tud_cdc_write_char('\n');
- }
- }
-
- tud_cdc_write_flush();
- }
-}
#endif
+}
//--------------------------------------------------------------------+
// Device Callbacks
@@ -356,24 +336,56 @@ void tud_resume_cb(void) {
// Host Callbacks
//--------------------------------------------------------------------+
-// Invoked when device is mounted (configured)
+// One flag per possible device address — set by tuh_mount_cb (host task) and
+// cleared by print_devinfo_task once the device's descriptors are printed.
+static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1];
+
+// Invoked when device is mounted (configured). Runs in the host task — keep
+// minimal; descriptor fetching happens in print_devinfo_task (different
+// context so sync helpers are safe).
void tuh_mount_cb(uint8_t daddr) {
printf("[HOST] Device attached, address = %d\r\n", daddr);
blink_interval_ms = BLINK_MOUNTED;
- print_device_info(daddr);
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = true;
+ }
}
// Invoked when device is unmounted (unplugged)
void tuh_umount_cb(uint8_t daddr) {
printf("[HOST] Device removed, address = %d\r\n", daddr);
blink_interval_ms = BLINK_NOT_MOUNTED;
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = false;
+ }
}
//--------------------------------------------------------------------+
-// Host Device Info
+// Host Device Info — serialises descriptor fetching across all mounted
+// devices using sync helpers. Safe to call from main loop (OS_NONE) or a
+// dedicated task (FreeRTOS) — but NOT from a host-stack callback.
//--------------------------------------------------------------------+
-static void print_device_info(uint8_t daddr) {
+void print_devinfo_task(void *param) {
+ (void) param;
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ while (1) {
+#endif
+ if (current_role == TUSB_ROLE_HOST) {
+ for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) {
+ if (need_devinfo[daddr]) {
+ need_devinfo[daddr] = false;
+ print_one_device(daddr);
+ }
+ }
+ }
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(pdMS_TO_TICKS(10));
+ }
+#endif
+}
+
+static void print_one_device(uint8_t daddr) {
// Get Device Descriptor
uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc.device, 18);
if (XFER_RESULT_SUCCESS != xfer_result) {
@@ -467,30 +479,23 @@ static void print_utf16(uint16_t* temp_buf, size_t buf_len) {
// Blinking Task
//--------------------------------------------------------------------+
-#if CFG_TUSB_OS == OPT_OS_FREERTOS
void led_blinking_task(void *param) {
(void) param;
+ static uint32_t start_ms = 0;
static bool led_state = false;
-
- // RTOS forever loop
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
while (1) {
- board_led_write(led_state);
- led_state = 1 - led_state; // toggle
vTaskDelay(pdMS_TO_TICKS(blink_interval_ms));
+ start_ms += blink_interval_ms;
+ board_led_write(led_state);
+ led_state = 1 - led_state;
}
-}
#else
-void led_blinking_task(void) {
- static uint32_t start_ms = 0;
- static bool led_state = false;
-
- // Blink every interval ms
if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
return; // not enough time
}
start_ms += blink_interval_ms;
-
board_led_write(led_state);
- led_state = 1 - led_state; // toggle
-}
+ led_state = 1 - led_state;
#endif
+}
diff --git a/examples/dual/host_info_to_device_cdc/src/main.c b/examples/dual/host_info_to_device_cdc/src/main.c
index cf3430464..5186f91dc 100644
--- a/examples/dual/host_info_to_device_cdc/src/main.c
+++ b/examples/dual/host_info_to_device_cdc/src/main.c
@@ -130,7 +130,7 @@ static void main_task(void* param) {
led_blinking_task();
// preempted RTOS run device/host stack in its own task
-#if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO
+#if CFG_TUSB_OS_HAS_SCHEDULER == 0
tud_task(); // tinyusb device task
tuh_task(); // tinyusb host task
#endif
@@ -140,7 +140,7 @@ static void main_task(void* param) {
int main(void) {
board_init();
-#if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO
+#if CFG_TUSB_OS_HAS_SCHEDULER == 0
printf("TinyUSB Host Information -> Device CDC Example\r\n");
usb_device_init();
@@ -156,7 +156,7 @@ int main(void) {
return 0;
}
-#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO
+#if CFG_TUSB_OS_HAS_SCHEDULER
// USB Device Driver task for RTOS
static void usb_device_task(void *param) {
(void) param;
diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c
index 544f38102..679ce6f43 100644
--- a/examples/host/bare_api/src/main.c
+++ b/examples/host/bare_api/src/main.c
@@ -48,14 +48,19 @@ CFG_TUH_MEM_SECTION uint16_t temp_buf[128]; // temp buffer for string descriptor
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
void led_blinking_task(void);
+void print_devinfo_task(void);
static void print_utf16(uint16_t *temp_buf, size_t buf_len);
-void print_device_descriptor(tuh_xfer_t* xfer);
+static void print_one_device(uint8_t daddr);
void parse_config_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
uint8_t* get_hid_buf(uint8_t daddr);
void free_hid_buf(uint8_t daddr);
+// One flag per possible device address — set in tuh_mount_cb (host task) and
+// cleared by print_devinfo_task (main loop) once the descriptors are printed.
+static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1];
+
/*------------- MAIN -------------*/
int main(void) {
board_init();
@@ -74,38 +79,53 @@ int main(void) {
while (1) {
// tinyusb host task
tuh_task();
+ print_devinfo_task();
led_blinking_task();
}
}
/*------------- TinyUSB Callbacks -------------*/
-// Invoked when device is mounted (configured)
+// Invoked when device is mounted (configured). Runs in the host task — keep
+// it minimal. The descriptor fetching/printing happens in print_devinfo_task()
+// below where the sync helpers are safe (different context).
void tuh_mount_cb(uint8_t daddr) {
printf("Device attached, address = %d\r\n", daddr);
-
- // Get Device Descriptor
- // TODO: invoking control transfer now has issue with mounting hub with multiple devices attached, fix later
- tuh_descriptor_get_device(daddr, &desc_device, 18, print_device_descriptor, 0);
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = true;
+ }
}
/// Invoked when device is unmounted (bus reset/unplugged)
void tuh_umount_cb(uint8_t daddr) {
printf("Device removed, address = %d\r\n", daddr);
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = false;
+ }
free_hid_buf(daddr);
}
//--------------------------------------------------------------------+
-// Device Descriptor
+// Print device info task — serialises descriptor fetching across all
+// mounted devices via sync helpers. Sync calls are safe here because this
+// runs in the main loop (outside any host-stack callback context).
//--------------------------------------------------------------------+
-void print_device_descriptor(tuh_xfer_t *xfer) {
- if (XFER_RESULT_SUCCESS != xfer->result) {
+void print_devinfo_task(void) {
+ for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) {
+ if (need_devinfo[daddr]) {
+ need_devinfo[daddr] = false;
+ print_one_device(daddr);
+ }
+ }
+}
+
+static void print_one_device(uint8_t daddr) {
+ // Get Device Descriptor
+ if (XFER_RESULT_SUCCESS != tuh_descriptor_get_device_sync(daddr, &desc_device, 18)) {
printf("Failed to get device descriptor\r\n");
return;
}
- uint8_t const daddr = xfer->daddr;
-
printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct);
printf("Device Descriptor:\r\n");
printf(" bLength %u\r\n" , desc_device.bLength);
@@ -119,7 +139,6 @@ void print_device_descriptor(tuh_xfer_t *xfer) {
printf(" idProduct 0x%04x\r\n" , desc_device.idProduct);
printf(" bcdDevice %04x\r\n" , desc_device.bcdDevice);
- // Get String descriptor using Sync API
printf(" iManufacturer %u ", desc_device.iManufacturer);
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf))) {
print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf));
diff --git a/examples/host/cdc_msc_hid/src/cdc_app.c b/examples/host/cdc_msc_hid/src/cdc_app.c
index 20033981e..e6c190715 100644
--- a/examples/host/cdc_msc_hid/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid/src/cdc_app.c
@@ -95,7 +95,6 @@ void tuh_cdc_mount_cb(uint8_t idx) {
printf("CDC Interface is mounted: address = %u, itf_num = %u\r\n", itf_info.daddr,
itf_info.desc.bInterfaceNumber);
-#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
// If CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined, line coding will be set by tinyusb stack
// while eneumerating new cdc device
cdc_line_coding_t line_coding = {0};
@@ -103,11 +102,6 @@ void tuh_cdc_mount_cb(uint8_t idx) {
printf(" Baudrate: %" PRIu32 ", Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);
printf(" Parity : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits);
}
-#else
- // Set Line Coding upon mounted
- cdc_line_coding_t new_line_coding = { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 };
- tuh_cdc_set_line_coding(idx, &new_line_coding, NULL, 0);
-#endif
}
// Invoked when a device with CDC interface is unmounted
diff --git a/examples/host/cdc_msc_hid_freertos/src/cdc_app.c b/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
index 30baacaac..9fad775e9 100644
--- a/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
@@ -29,16 +29,16 @@
#include "app.h"
#ifdef ESP_PLATFORM
- #define CDC_STACK_SZIE 2048
+ #define CDC_STACK_SIZE 2048
#else
- #define CDC_STACK_SZIE (3*configMINIMAL_STACK_SIZE/2)
+ #define CDC_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2)
#endif
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
#if configSUPPORT_STATIC_ALLOCATION
-StackType_t cdc_stack[CDC_STACK_SZIE];
+StackType_t cdc_stack[CDC_STACK_SIZE];
StaticTask_t cdc_taskdef;
#endif
@@ -46,9 +46,9 @@ static void cdc_app_task(void* param);
void cdc_app_init(void) {
#if configSUPPORT_STATIC_ALLOCATION
- (void) xTaskCreateStatic(cdc_app_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef);
+ (void) xTaskCreateStatic(cdc_app_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef);
#else
- (void) xTaskCreate(cdc_app_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, NULL);
+ (void) xTaskCreate(cdc_app_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES-2, NULL);
#endif
}
diff --git a/examples/host/device_info/src/main.c b/examples/host/device_info/src/main.c
index b0e38dd6b..f32ed1a3e 100644
--- a/examples/host/device_info/src/main.c
+++ b/examples/host/device_info/src/main.c
@@ -72,8 +72,14 @@ CFG_TUH_MEM_SECTION struct {
} desc;
void led_blinking_task(void* param);
+void print_devinfo_task(void* param);
static void print_utf16(uint16_t* temp_buf, size_t buf_len);
+// One flag per possible device address — set by tuh_mount_cb (host task) and
+// cleared by print_devinfo_task (separate task / main loop) once the device's
+// descriptor info has been printed.
+static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1];
+
#if CFG_TUSB_OS == OPT_OS_FREERTOS
void init_freertos_task(void);
#endif
@@ -103,6 +109,7 @@ int main(void) {
init_tinyusb();
while (1) {
tuh_task(); // tinyusb host task
+ print_devinfo_task(NULL);
led_blinking_task(NULL);
}
#endif
@@ -110,10 +117,34 @@ int main(void) {
/*------------- TinyUSB Callbacks -------------*/
-// Invoked when device is mounted (configured)
+// Invoked when device is mounted (configured). Runs in the host task — keep
+// it minimal. The actual descriptor fetching/printing happens in
+// print_devinfo_task() below, which runs in a different context (main loop
+// on OS_NONE / dedicated task on RTOS) where the sync helpers are safe.
void tuh_mount_cb(uint8_t daddr) {
blink_interval_ms = BLINK_MOUNTED;
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = true;
+ }
+}
+// Invoked when device is unmounted (bus reset/unplugged)
+void tuh_umount_cb(uint8_t daddr) {
+ blink_interval_ms = BLINK_NOT_MOUNTED;
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = false;
+ }
+ printf("Device removed, address = %d\r\n", daddr);
+}
+
+//--------------------------------------------------------------------+
+// Print device info task — serialises descriptor fetching across all
+// mounted devices using the sync helpers. Sync calls are safe here because
+// this task runs outside the host-task callback context (main loop on
+// OS_NONE / dedicated FreeRTOS task on RTOS).
+//--------------------------------------------------------------------+
+
+static void print_one_device(uint8_t daddr) {
// Get Device Descriptor
uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc.device, 18);
if (XFER_RESULT_SUCCESS != xfer_result) {
@@ -129,9 +160,8 @@ void tuh_mount_cb(uint8_t daddr) {
}
if (XFER_RESULT_SUCCESS != xfer_result) {
uint16_t* serial = (uint16_t*)(uintptr_t) desc.serial;
-
serial[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * 1 + 2));
- serial[1] = '0'; // simply 0
+ serial[1] = '0';
serial[2] = 0;
}
print_utf16((uint16_t*)(uintptr_t) desc.serial, sizeof(desc.serial)/2);
@@ -149,12 +179,9 @@ void tuh_mount_cb(uint8_t daddr) {
printf(" idProduct 0x%04x\r\n", desc.device.idProduct);
printf(" bcdDevice %04x\r\n", desc.device.bcdDevice);
- // Get String descriptor using Sync API
-
printf(" iManufacturer %u ", desc.device.iManufacturer);
if (desc.device.iManufacturer != 0) {
- xfer_result = tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf));
- if (XFER_RESULT_SUCCESS == xfer_result) {
+ if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf))) {
print_utf16((uint16_t*)(uintptr_t) desc.buf, sizeof(desc.buf)/2);
}
}
@@ -162,22 +189,33 @@ void tuh_mount_cb(uint8_t daddr) {
printf(" iProduct %u ", desc.device.iProduct);
if (desc.device.iProduct != 0) {
- xfer_result = tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf));
- if (XFER_RESULT_SUCCESS == xfer_result) {
+ if (XFER_RESULT_SUCCESS == tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf))) {
print_utf16((uint16_t*)(uintptr_t) desc.buf, sizeof(desc.buf)/2);
}
}
printf("\r\n");
printf(" iSerialNumber %u ", desc.device.iSerialNumber);
- printf("%s\r\n", (char*)desc.serial); // serial is already to UTF-8
+ printf("%s\r\n", (char*)desc.serial); // serial is already UTF-8
printf(" bNumConfigurations %u\r\n", desc.device.bNumConfigurations);
}
-// Invoked when device is unmounted (bus reset/unplugged)
-void tuh_umount_cb(uint8_t daddr) {
- blink_interval_ms = BLINK_NOT_MOUNTED;
- printf("Device removed, address = %d\r\n", daddr);
+void print_devinfo_task(void* param) {
+ (void) param;
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ while (1) {
+#endif
+ for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) {
+ if (need_devinfo[daddr]) {
+ need_devinfo[daddr] = false;
+ print_one_device(daddr);
+ }
+ }
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(pdMS_TO_TICKS(10));
+ }
+#endif
}
//--------------------------------------------------------------------+
@@ -278,6 +316,9 @@ StaticTask_t blinky_taskdef;
StackType_t usb_stack[USB_STACK_SIZE];
StaticTask_t usb_taskdef;
+
+StackType_t devinfo_stack[USB_STACK_SIZE];
+StaticTask_t devinfo_taskdef;
#endif
#ifdef ESP_PLATFORM
@@ -299,9 +340,11 @@ void init_freertos_task(void) {
#if configSUPPORT_STATIC_ALLOCATION
xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
xTaskCreateStatic(usb_host_task, "usbh", USB_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_stack, &usb_taskdef);
+ xTaskCreateStatic(print_devinfo_task, "devinfo", USB_STACK_SIZE, NULL, configMAX_PRIORITIES-2, devinfo_stack, &devinfo_taskdef);
#else
xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(usb_host_task, "usbh", USB_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+ xTaskCreate(print_devinfo_task, "devinfo", USB_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
#endif
// only start scheduler for non-espressif mcu
diff --git a/examples/host/midi2_host/src/main.c b/examples/host/midi2_host/src/main.c
index 63b08318c..d81e77a33 100644
--- a/examples/host/midi2_host/src/main.c
+++ b/examples/host/midi2_host/src/main.c
@@ -35,7 +35,7 @@
// State
//--------------------------------------------------------------------+
-static uint8_t midi2_idx = 0xFF;
+TU_ATTR_UNUSED static uint8_t midi2_idx = 0xFF;
//--------------------------------------------------------------------+
// UMP printer - shows MT and word(s) in hex; decodes Channel Voice
@@ -160,6 +160,4 @@ int main(void) {
while (1) {
tuh_task();
}
-
- return 0;
}
diff --git a/examples/host/msc_file_explorer_freertos/skip.txt b/examples/host/msc_file_explorer_freertos/skip.txt
index f0be07d25..a8c9bea2a 100644
--- a/examples/host/msc_file_explorer_freertos/skip.txt
+++ b/examples/host/msc_file_explorer_freertos/skip.txt
@@ -1,3 +1,4 @@
mcu:CH32F20X
board:lpcxpresso54114
mcu:FT90X
+board:stm32h7s3nucleo
diff --git a/hw/bsp/BoardPresets.json b/hw/bsp/BoardPresets.json
index 1ff29f99d..2ad940caf 100644
--- a/hw/bsp/BoardPresets.json
+++ b/hw/bsp/BoardPresets.json
@@ -6,7 +6,7 @@
"hidden": true,
"description": "Configure preset for the ${presetName} board",
"generator": "Ninja Multi-Config",
- "binaryDir": "${sourceDir}/build/${presetName}",
+ "binaryDir": "${sourceDir}/cmake-build-${presetName}",
"cacheVariables": {
"CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo",
"BOARD": "${presetName}"
@@ -17,7 +17,7 @@
"hidden": true,
"description": "Configure preset for the ${presetName} board",
"generator": "Ninja",
- "binaryDir": "${sourceDir}/build/${presetName}",
+ "binaryDir": "${sourceDir}/cmake-build-${presetName}",
"cacheVariables": {
"BOARD": "${presetName}"
}
@@ -123,6 +123,10 @@
"inherits": "default"
},
{
+ "name": "ch32v103c_bluepill",
+ "inherits": "default"
+ },
+ {
"name": "ch32v103r_r1_1v0",
"inherits": "default"
},
@@ -1092,6 +1096,11 @@
"configurePreset": "ch32f205r-r0"
},
{
+ "name": "ch32v103c_bluepill",
+ "description": "Build preset for the ch32v103c_bluepill board",
+ "configurePreset": "ch32v103c_bluepill"
+ },
+ {
"name": "ch32v103r_r1_1v0",
"description": "Build preset for the ch32v103r_r1_1v0 board",
"configurePreset": "ch32v103r_r1_1v0"
@@ -2482,6 +2491,19 @@
]
},
{
+ "name": "ch32v103c_bluepill",
+ "steps": [
+ {
+ "type": "configure",
+ "name": "ch32v103c_bluepill"
+ },
+ {
+ "type": "build",
+ "name": "ch32v103c_bluepill"
+ }
+ ]
+ },
+ {
"name": "ch32v103r_r1_1v0",
"steps": [
{
diff --git a/hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.cmake b/hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.cmake
new file mode 100644
index 000000000..f6e47ba30
--- /dev/null
+++ b/hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.cmake
@@ -0,0 +1,8 @@
+set(LD_FLASH_SIZE 64K)
+set(LD_RAM_SIZE 20K)
+
+function(update_board TARGET)
+ target_compile_definitions(${TARGET} PUBLIC
+ CFG_EXAMPLE_MSC_DUAL_READONLY
+ )
+endfunction()
diff --git a/hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.h b/hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.h
new file mode 100644
index 000000000..3a97b44d6
--- /dev/null
+++ b/hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.h
@@ -0,0 +1,27 @@
+/* Some Chinese manufacturers use CH32V103C8T6 to make Bluepill boards
+ */
+/* metadata:
+ name: CH32V103C8T6-Bluepill
+ url: https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill
+*/
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LED_PORT GPIOC
+#define LED_PIN GPIO_Pin_13
+#define LED_STATE_ON 0
+
+#define BUTTON_PORT GPIOA
+#define BUTTON_PIN GPIO_Pin_1
+#define BUTTON_STATE_ACTIVE 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.mk b/hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.mk
new file mode 100644
index 000000000..e594f42a7
--- /dev/null
+++ b/hw/bsp/ch32v10x/boards/ch32v103c_bluepill/board.mk
@@ -0,0 +1,5 @@
+CFLAGS += -DCFG_EXAMPLE_MSC_DUAL_READONLY
+
+LDFLAGS += \
+ -Wl,--defsym=__FLASH_SIZE=64K \
+ -Wl,--defsym=__RAM_SIZE=20K \
diff --git a/hw/bsp/ch32v10x/family.c b/hw/bsp/ch32v10x/family.c
index 72dae7086..9f5dc5572 100644
--- a/hw/bsp/ch32v10x/family.c
+++ b/hw/bsp/ch32v10x/family.c
@@ -66,14 +66,33 @@ uint32_t tusb_time_millis_api(void) {
}
#endif
+// 0x800 CSR register is writable in U-mode
+// according to manual: https://www.wch-ic.com/downloads/QingKeV3_Processor_Manual_PDF.html
+__attribute__((always_inline)) RV_STATIC_INLINE
+void __wch_vendor_enable_irq(void)
+{
+ __asm volatile ("csrs 0x800, %0" : : "r" (0x88) );
+}
+
+__attribute__((always_inline)) RV_STATIC_INLINE
+void __wch_vendor_disable_irq(void)
+{
+ __asm volatile ("csrc 0x800, %0" : : "r" (0x88) );
+ __asm volatile ("fence.i");
+}
+
void board_init(void) {
- __disable_irq();
+ /* __disable_irq() in CH32V103 EVT attempts to call
+ * `csrc mstatus, 0x88` in U-mode, which is allowed ONLY in M-mode.
+ * Replace this with CSR 0x800 to avoid hard-fault. */
+ __wch_vendor_disable_irq();
#if CFG_TUSB_OS == OPT_OS_NONE
SysTick_Config(SystemCoreClock / 1000);
#endif
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
+ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
EXTEN->EXTEN_CTR |= EXTEN_USBFS_IO_EN;
uint8_t usb_div;
@@ -123,7 +142,7 @@ void board_init(void) {
USART_Init(USART1, &usart);
USART_Cmd(USART1, ENABLE);
- __enable_irq();
+ __wch_vendor_enable_irq();
board_led_write(true);
}
diff --git a/hw/bsp/espressif/family.cmake b/hw/bsp/espressif/family.cmake
index 30d5a6ac9..b3bda4ad8 100644
--- a/hw/bsp/espressif/family.cmake
+++ b/hw/bsp/espressif/family.cmake
@@ -44,3 +44,10 @@ set(EXTRA_COMPONENT_DIRS "src" "${CMAKE_CURRENT_LIST_DIR}/boards" "${CMAKE_CURRE
set(SDKCONFIG ${CMAKE_BINARY_DIR}/sdkconfig)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+
+# CI_BUILD marks firmware built in CI (GitHub Actions sets CI). Mirrors the
+# non-espressif define added in family_configure_common(); applied build-wide
+# here since espressif examples return before that function runs.
+if(DEFINED ENV{CI})
+ idf_build_set_property(COMPILE_DEFINITIONS "CI_BUILD=1" APPEND)
+endif()
diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake
index 2468ac43c..1f3952205 100644
--- a/hw/bsp/family_support.cmake
+++ b/hw/bsp/family_support.cmake
@@ -244,7 +244,7 @@ function(family_add_bloaty TARGET)
COMMAND ${BLOATY_EXE} ${OPTION_LIST} $<TARGET_FILE:${TARGET}>
VERBATIM)
- set_property(TARGET ${TARGET}-bloaty PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-bloaty PROPERTY FOLDER ${TARGET}-group)
# post build
# add_custom_command(TARGET ${TARGET} POST_BUILD
# COMMAND ${BLOATY_EXE} --csv ${OPTION_LIST} $<TARGET_FILE:${TARGET}> > ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_bloaty.csv
@@ -265,7 +265,7 @@ function(family_add_linkermap TARGET)
VERBATIM
)
- set_property(TARGET ${TARGET}-linkermap PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-linkermap PROPERTY FOLDER ${TARGET}-group)
# post build
add_custom_command(TARGET ${TARGET} POST_BUILD
@@ -347,7 +347,7 @@ echo \"$MEMBROWSE_CMD\"")
COMMAND ${CMAKE_COMMAND} -E env MEMBROWSE_UPLOAD=0 bash -lc "${MEMBROWSE_PREPARE_CMD}; eval \"$MEMBROWSE_CMD\""
VERBATIM
)
- set_property(TARGET ${TARGET}-membrowse PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-membrowse PROPERTY FOLDER ${TARGET}-group)
add_custom_target(${TARGET}-membrowse-upload
COMMAND ${CMAKE_COMMAND} -E env MEMBROWSE_UPLOAD=1 bash -lc "${MEMBROWSE_PREPARE_CMD}; eval \"$MEMBROWSE_CMD\""
@@ -359,7 +359,7 @@ echo \"$MEMBROWSE_CMD\"")
endif ()
add_dependencies(examples-membrowse-upload ${TARGET}-membrowse-upload)
- set_property(TARGET ${TARGET}-membrowse-upload PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-membrowse-upload PROPERTY FOLDER ${TARGET}-group)
endif ()
endfunction()
@@ -454,6 +454,12 @@ function(family_configure_common TARGET RTOS)
BOARD_${BOARD_UPPER}
)
+ # CI_BUILD marks firmware built in CI (GitHub Actions sets CI). Examples can use
+ # it to alter behavior under test, e.g. board_test idles to park HIL boards.
+ if(DEFINED ENV{CI})
+ target_compile_definitions(${TARGET} PUBLIC CI_BUILD=1)
+ endif()
+
# compile define from command line
if(DEFINED CFLAGS_CLI)
separate_arguments(CFLAGS_CLI)
@@ -648,7 +654,7 @@ exit"
VERBATIM
)
- set_property(TARGET ${NAME_TARGET}-jlink PROPERTY FOLDER ${TARGET}-group)
+# set_property(TARGET ${NAME_TARGET}-jlink PROPERTY FOLDER ${NAME_TARGET}-group)
endfunction()
@@ -663,7 +669,7 @@ function(family_flash_stlink TARGET)
COMMAND ${STM32_PROGRAMMER_CLI} --connect port=swd --write $<TARGET_FILE:${TARGET}> --go
)
- set_property(TARGET ${TARGET}-stlink PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-stlink PROPERTY FOLDER ${TARGET}-group)
endfunction()
@@ -678,7 +684,7 @@ function(family_flash_stflash TARGET)
COMMAND ${ST_FLASH} write $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.bin 0x8000000
)
- set_property(TARGET ${TARGET}-stflash PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-stflash PROPERTY FOLDER ${TARGET}-group)
endfunction()
@@ -706,7 +712,7 @@ function(family_flash_openocd TARGET)
VERBATIM
)
- set_property(TARGET ${TARGET}-openocd PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-openocd PROPERTY FOLDER ${TARGET}-group)
endfunction()
@@ -769,7 +775,7 @@ function(family_flash_wlink_rs TARGET)
COMMAND ${WLINK_RS} flash $<TARGET_FILE:${TARGET}>
)
- set_property(TARGET ${TARGET}-wlink-rs PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-wlink-rs PROPERTY FOLDER ${TARGET}-group)
endfunction()
@@ -784,7 +790,7 @@ function(family_flash_pyocd TARGET)
COMMAND ${PYOCD} flash -t ${PYOCD_TARGET} $<TARGET_FILE:${TARGET}>
)
- set_property(TARGET ${TARGET}-pyocd PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-pyocd PROPERTY FOLDER ${TARGET}-group)
endfunction()
@@ -794,7 +800,7 @@ function(family_flash_uf2 TARGET FAMILY_ID)
DEPENDS ${TARGET}
COMMAND python ${UF2CONV_PY} -f ${FAMILY_ID} --deploy $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.uf2
)
- set_property(TARGET ${TARGET}-uf2 PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-uf2 PROPERTY FOLDER ${TARGET}-group)
endfunction()
@@ -810,7 +816,7 @@ function(family_flash_teensy TARGET)
COMMAND ${TEENSY_CLI} --mcu=${TEENSY_MCU} -w -s $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.hex
)
- set_property(TARGET ${TARGET}-teensy PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-teensy PROPERTY FOLDER ${TARGET}-group)
endfunction()
@@ -830,7 +836,7 @@ function(family_flash_nxplink TARGET)
COMMAND ${LINKSERVER_PATH} flash ${NXPLINK_DEVICE} load $<TARGET_FILE:${TARGET}>
)
- set_property(TARGET ${TARGET}-nxplink PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-nxplink PROPERTY FOLDER ${TARGET}-group)
endfunction()
@@ -845,7 +851,7 @@ function(family_flash_dfu_util TARGET OPTION)
VERBATIM
)
- set_property(TARGET ${TARGET}-dfu-util PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-dfu-util PROPERTY FOLDER ${TARGET}-group)
endfunction()
function(family_flash_msp430flasher TARGET)
@@ -862,7 +868,7 @@ function(family_flash_msp430flasher TARGET)
${MSP430FLASHER} -w $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.hex -z [VCC]
)
- set_property(TARGET ${TARGET}-msp430flasher PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-msp430flasher PROPERTY FOLDER ${TARGET}-group)
endfunction()
function(family_flash_rfp TARGET)
@@ -880,7 +886,7 @@ function(family_flash_rfp TARGET)
VERBATIM
)
- set_property(TARGET ${TARGET}-rfp PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-rfp PROPERTY FOLDER ${TARGET}-group)
endfunction()
@@ -897,7 +903,27 @@ function(family_flash_uniflash TARGET)
VERBATIM
)
- set_property(TARGET ${TARGET}-uniflash PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-uniflash PROPERTY FOLDER ${TARGET}-group)
+endfunction()
+
+# Add flash lm4flash target (lightweight flasher for TI Tiva-C/Stellaris ICDI boards)
+function(family_flash_lm4flash TARGET)
+ if (NOT DEFINED LM4FLASH)
+ set(LM4FLASH lm4flash)
+ endif ()
+
+ if (NOT DEFINED LM4FLASH_OPTION)
+ set(LM4FLASH_OPTION "")
+ endif ()
+ separate_arguments(OPTION_LIST UNIX_COMMAND ${LM4FLASH_OPTION})
+
+ add_custom_target(${TARGET}-lm4flash
+ DEPENDS ${TARGET}
+ COMMAND ${LM4FLASH} ${OPTION_LIST} $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.bin
+ VERBATIM
+ )
+
+ #set_property(TARGET ${TARGET}-lm4flash PROPERTY FOLDER ${TARGET}-group)
endfunction()
# Add flash ft9xx target need to remove kernal's ftdi_sio and bind D2XX drivers
@@ -912,7 +938,7 @@ function(family_flash_ft9xx TARGET)
COMMAND ${FT9XXPROG} -f $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.bin
)
- set_property(TARGET ${TARGET}-ft9xx PROPERTY FOLDER ${TARGET}-group)
+ #set_property(TARGET ${TARGET}-ft9xx PROPERTY FOLDER ${TARGET}-group)
endfunction()
#----------------------------------
diff --git a/hw/bsp/stm32f4/family.c b/hw/bsp/stm32f4/family.c
index 4eea5c7a8..3a323e673 100644
--- a/hw/bsp/stm32f4/family.c
+++ b/hw/bsp/stm32f4/family.c
@@ -109,6 +109,11 @@ void USARTn_IRQHandler(void) {
void board_init(void) {
board_clock_init();
+
+ __HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
+ __HAL_FLASH_DATA_CACHE_ENABLE();
+ __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
+
//SystemCoreClockUpdate();
// Enable All GPIOs clocks
diff --git a/hw/bsp/stm32f7/boards/stm32f746disco/board.cmake b/hw/bsp/stm32f7/boards/stm32f746disco/board.cmake
index bc26c6ef4..5f46365f4 100644
--- a/hw/bsp/stm32f7/boards/stm32f746disco/board.cmake
+++ b/hw/bsp/stm32f7/boards/stm32f746disco/board.cmake
@@ -1,5 +1,5 @@
set(MCU_VARIANT stm32f746xx)
-set(JLINK_DEVICE stm32f746xx)
+set(JLINK_DEVICE stm32f746ng)
set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/STM32F746ZGTx_FLASH.ld)
diff --git a/hw/bsp/stm32f7/boards/stm32f769disco/board.cmake b/hw/bsp/stm32f7/boards/stm32f769disco/board.cmake
index 2335b869e..dbdd07e4d 100644
--- a/hw/bsp/stm32f7/boards/stm32f769disco/board.cmake
+++ b/hw/bsp/stm32f7/boards/stm32f769disco/board.cmake
@@ -1,5 +1,6 @@
set(MCU_VARIANT stm32f769xx)
set(JLINK_DEVICE stm32f769ni)
+#set(JLINK_OPTION "-USB 000778170924")
set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/STM32F769ZITx_FLASH.ld)
diff --git a/hw/bsp/stm32f7/family.c b/hw/bsp/stm32f7/family.c
index 7a322591b..9427ac4a6 100644
--- a/hw/bsp/stm32f7/family.c
+++ b/hw/bsp/stm32f7/family.c
@@ -82,8 +82,9 @@ static UART_HandleTypeDef UartHandle = {.Instance = USARTn,
.OverSampling = UART_OVERSAMPLING_16,
}};
-// RX ring buffer via RXNE interrupt — no HAL IT functions used (avoid HAL state conflicts)
-static uint8_t uart_rx_ff_buf[32];
+// RX ring buffer via RXNE interrupt — no HAL IT functions used (avoid HAL state conflicts).
+// Sized to absorb a full host-forwarding burst (>64B) when the main loop briefly stalls.
+static uint8_t uart_rx_ff_buf[256];
static tu_fifo_t uart_rx_ff;
void USARTn_IRQHandler(void) {
@@ -142,13 +143,24 @@ void board_init(void) {
// 1ms tick timer
SysTick_Config(SystemCoreClock / 1000);
+ // Set UART interrupt higher priority than USB OTG since the F7 USART has no hardware RX FIFO, so a host example's
+ // UART RX must not be starved by the frequent USB host interrupts or incoming bytes overrun (ORE) and dropped.
+ NVIC_SetPriority(OTG_FS_IRQn, 1);
+ NVIC_SetPriority(OTG_HS_IRQn, 1);
+ #ifdef UART_ID
+ NVIC_SetPriority(USARTn_IRQn, 0);
+ #endif
+
#elif CFG_TUSB_OS == OPT_OS_FREERTOS
// Explicitly disable systick to prevent its ISR from running before scheduler start
SysTick->CTRL &= ~1U;
// If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
- NVIC_SetPriority(OTG_FS_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
- NVIC_SetPriority(OTG_HS_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
+ NVIC_SetPriority(OTG_FS_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1);
+ NVIC_SetPriority(OTG_HS_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1);
+ #ifdef UART_ID
+ NVIC_SetPriority(USARTn_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
+ #endif
#endif
#ifdef UART_ID
@@ -156,7 +168,6 @@ void board_init(void) {
HAL_UART_Init(&UartHandle);
tu_fifo_config(&uart_rx_ff, uart_rx_ff_buf, sizeof(uart_rx_ff_buf), false);
USARTn->CR1 |= USART_CR1_RXNEIE;
- NVIC_SetPriority(USARTn_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
NVIC_EnableIRQ(USARTn_IRQn);
#endif
diff --git a/hw/bsp/stm32u0/family.c b/hw/bsp/stm32u0/family.c
index 7bd99fba6..0d20ba43f 100644
--- a/hw/bsp/stm32u0/family.c
+++ b/hw/bsp/stm32u0/family.c
@@ -166,6 +166,19 @@ uint32_t board_button_read(void) {
return BUTTON_STATE_ACTIVE == HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_PIN);
}
+size_t board_get_unique_id(uint8_t id[], size_t max_len) {
+ (void) max_len;
+ volatile uint32_t *stm32_uuid = (volatile uint32_t *) UID_BASE;
+ uint32_t *id32 = (uint32_t *) (uintptr_t) id;
+ uint8_t const len = 12;
+
+ id32[0] = stm32_uuid[0];
+ id32[1] = stm32_uuid[1];
+ id32[2] = stm32_uuid[2];
+
+ return len;
+}
+
int board_uart_read(uint8_t* buf, int len) {
#ifdef UART_ID
int count = 0;
diff --git a/hw/bsp/tm4c/family.cmake b/hw/bsp/tm4c/family.cmake
index 41b8a597a..ce7c62409 100644
--- a/hw/bsp/tm4c/family.cmake
+++ b/hw/bsp/tm4c/family.cmake
@@ -79,6 +79,6 @@ function(family_configure_example TARGET RTOS)
# Flashing
family_add_bin_hex(${TARGET})
family_flash_jlink(${TARGET})
- family_flash_openocd(${TARGET})
- family_flash_uniflash(${TARGET})
+ family_flash_lm4flash(${TARGET})
+ # family_flash_uniflash(${TARGET})
endfunction()
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 62c313b83..4441222c8 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -99,6 +99,7 @@ typedef struct {
typedef struct {
TUH_EPBUF_DEF(tx, CFG_TUH_CDC_TX_EPSIZE);
TUH_EPBUF_DEF(rx, CFG_TUH_CDC_RX_EPSIZE);
+ TUH_EPBUF_DEF(ctrl, 8);
} cdch_epbuf_t;
static cdch_interface_t cdch_data[CFG_TUH_CDC];
@@ -1003,15 +1004,16 @@ static bool acm_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_
.wLength = tu_htole16((uint16_t) sizeof(cdc_line_coding_t))
};
- // use usbh enum buf to hold line coding since user line_coding variable does not live long enough
- uint8_t *enum_buf = usbh_get_enum_buf();
- memcpy(enum_buf, &p_cdc->requested_line.coding, sizeof(cdc_line_coding_t));
+ // use local ctrl buf to hold line coding since user line_coding variable does not live long enough
+ uint8_t const idx = get_idx_by_ptr(p_cdc);
+ uint8_t *ctrl_buf = cdch_epbuf[idx].ctrl;
+ memcpy(ctrl_buf, &p_cdc->requested_line.coding, sizeof(cdc_line_coding_t));
tuh_xfer_t xfer = {
.daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request,
- .buffer = enum_buf,
+ .buffer = ctrl_buf,
.complete_cb = complete_cb,
.user_data = user_data
};
@@ -1491,7 +1493,7 @@ static inline uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc) {
//------------- Control Request -------------//
static bool cp210x_set_request(cdch_interface_t * p_cdc, uint8_t command, uint16_t value,
- uint8_t * buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint8_t const * buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
@@ -1504,19 +1506,20 @@ static bool cp210x_set_request(cdch_interface_t * p_cdc, uint8_t command, uint16
.wLength = tu_htole16(length)
};
- // use usbh enum buf since application variable does not live long enough
- uint8_t * enum_buf = NULL;
+ // use local ctrl buf since application variable does not live long enough
+ uint8_t * ctrl_buf = NULL;
if (buffer && length > 0) {
- enum_buf = usbh_get_enum_buf();
- tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ uint8_t const idx = get_idx_by_ptr(p_cdc);
+ ctrl_buf = cdch_epbuf[idx].ctrl;
+ tu_memcpy_s(ctrl_buf, sizeof(cdch_epbuf[idx].ctrl), buffer, length);
}
tuh_xfer_t xfer = {
.daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request,
- .buffer = enum_buf,
+ .buffer = ctrl_buf,
.complete_cb = complete_cb,
.user_data = user_data
};
@@ -1563,7 +1566,7 @@ static void cp210x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t
static bool cp210x_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
// Not every baud rate is supported. See datasheets and AN205 "CP210x Baud Rate Support"
uint32_t baud_le = tu_htole32(p_cdc->requested_line.coding.bit_rate);
- return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, complete_cb, user_data);
+ return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t const *) &baud_le, 4, complete_cb, user_data);
}
static bool cp210x_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
@@ -1640,7 +1643,7 @@ static uint16_t ch34x_get_divisor_prescaler(cdch_interface_t *p_cdc);
//------------- Control Request -------------//
static bool ch34x_set_request(cdch_interface_t *p_cdc, uint8_t direction, uint8_t request,
- uint16_t value, uint16_t index, uint8_t *buffer, uint16_t length,
+ uint16_t value, uint16_t index, uint8_t const *buffer, uint16_t length,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request_setup = {
.bmRequestType_bit = {
@@ -1654,13 +1657,14 @@ static bool ch34x_set_request(cdch_interface_t *p_cdc, uint8_t direction, uint8_
.wLength = tu_htole16(length)
};
- // use usbh enum buf since application variable does not live long enough
- uint8_t *enum_buf = NULL;
+ // use local ctrl buf since application variable does not live long enough
+ uint8_t *ctrl_buf = NULL;
- if (buffer && length > 0) {
- enum_buf = usbh_get_enum_buf();
- if (direction == TUSB_DIR_OUT) {
- tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ if (length > 0) {
+ uint8_t const idx = get_idx_by_ptr(p_cdc);
+ ctrl_buf = cdch_epbuf[idx].ctrl;
+ if (buffer && direction == TUSB_DIR_OUT) {
+ tu_memcpy_s(ctrl_buf, sizeof(cdch_epbuf[idx].ctrl), buffer, length);
}
}
@@ -1668,7 +1672,7 @@ static bool ch34x_set_request(cdch_interface_t *p_cdc, uint8_t direction, uint8_
.daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request_setup,
- .buffer = enum_buf,
+ .buffer = ctrl_buf,
.complete_cb = complete_cb,
.user_data = user_data
};
@@ -1682,8 +1686,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool ch34x_control_out(cdch_interface_t *p_c
}
TU_ATTR_ALWAYS_INLINE static inline bool ch34x_control_in(cdch_interface_t *p_cdc, uint8_t request, uint16_t value, uint16_t index,
- uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize,
+ uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, NULL, buffersize,
complete_cb, user_data);
}
@@ -1692,12 +1696,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool ch34x_write_reg(cdch_interface_t *p_cdc
return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data);
}
-//static bool ch34x_read_reg_request ( cdch_interface_t * p_cdc, uint16_t reg,
-// uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
-//{
-// return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data );
-//}
-
//------------- Driver API -------------//
// internal control complete to update state such as line state, encoding
@@ -1794,8 +1792,7 @@ static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
switch (state) {
case CONFIG_CH34X_READ_VERSION: {
- uint8_t* enum_buf = usbh_get_enum_buf();
- TU_ASSERT(ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, enum_buf, 2,
+ TU_ASSERT(ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, 2,
cdch_process_set_config, CONFIG_CH34X_SERIAL_INIT));
break;
}
@@ -1950,7 +1947,7 @@ static bool pl2303_encode_baud_rate(cdch_interface_t *p_cdc, uint8_t buf[PL2303_
//------------- Control Request -------------//
static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t requesttype,
- uint16_t value, uint16_t index, uint8_t *buffer, uint16_t length,
+ uint16_t value, uint16_t index, uint8_t const *buffer, uint16_t length,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request_setup = {
.bmRequestType = requesttype,
@@ -1960,13 +1957,14 @@ static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t
.wLength = tu_htole16(length)
};
- // use usbh enum buf since application variable does not live long enough
- uint8_t *enum_buf = NULL;
+ // use local ctrl buf since application variable does not live long enough
+ uint8_t *ctrl_buf = NULL;
- if (buffer && length > 0) {
- enum_buf = usbh_get_enum_buf();
- if (request_setup.bmRequestType_bit.direction == TUSB_DIR_OUT) {
- tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ if (length > 0) {
+ uint8_t const idx = get_idx_by_ptr(p_cdc);
+ ctrl_buf = cdch_epbuf[idx].ctrl;
+ if (buffer && request_setup.bmRequestType_bit.direction == TUSB_DIR_OUT) {
+ tu_memcpy_s(ctrl_buf, sizeof(cdch_epbuf[idx].ctrl), buffer, length);
}
}
@@ -1974,7 +1972,7 @@ static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t
.daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request_setup,
- .buffer = enum_buf,
+ .buffer = ctrl_buf,
.complete_cb = complete_cb,
.user_data = user_data
};
@@ -1982,10 +1980,10 @@ static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t
return tuh_control_xfer(&xfer);
}
-static bool pl2303_vendor_read(cdch_interface_t *p_cdc, uint16_t value, uint8_t *buf,
+static bool pl2303_vendor_read(cdch_interface_t *p_cdc, uint16_t value,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint8_t request = p_cdc->pl2303.type == PL2303_TYPE_HXN ? PL2303_VENDOR_READ_NREQUEST : PL2303_VENDOR_READ_REQUEST;
- return pl2303_set_request(p_cdc, request, PL2303_VENDOR_READ_REQUEST_TYPE, value, 0, buf, 1, complete_cb, user_data);
+ return pl2303_set_request(p_cdc, request, PL2303_VENDOR_READ_REQUEST_TYPE, value, 0, NULL, 1, complete_cb, user_data);
}
static bool pl2303_vendor_write(cdch_interface_t *p_cdc, uint16_t value, uint16_t index,
@@ -1995,9 +1993,8 @@ static bool pl2303_vendor_write(cdch_interface_t *p_cdc, uint16_t value, uint16_
}
static inline bool pl2303_supports_hx_status(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- uint8_t buf = 0;
return pl2303_set_request(p_cdc, PL2303_VENDOR_READ_REQUEST, PL2303_VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS, 0,
- &buf, 1, complete_cb, user_data);
+ NULL, 1, complete_cb, user_data);
}
//static bool pl2303_get_line_request(cdch_interface_t * p_cdc, uint8_t buf[PL2303_LINE_CODING_BUFSIZE]) {
@@ -2131,7 +2128,6 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
// state CONFIG_PL2303_READ1 may have no success due to expected stall by pl2303_supports_hx_status()
const uintptr_t state = xfer->user_data;
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS || state == CONFIG_PL2303_READ1);
- uint8_t* enum_buf = usbh_get_enum_buf();
pl2303_type_t type;
switch (state) {
@@ -2162,7 +2158,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_WRITE1));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, cdch_process_set_config, CONFIG_PL2303_WRITE1));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2178,7 +2174,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
case CONFIG_PL2303_READ2:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_READ3));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, cdch_process_set_config, CONFIG_PL2303_READ3));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2186,7 +2182,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
case CONFIG_PL2303_READ3:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, enum_buf, cdch_process_set_config, CONFIG_PL2303_READ4));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, cdch_process_set_config, CONFIG_PL2303_READ4));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2194,7 +2190,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
case CONFIG_PL2303_READ4:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_WRITE2));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, cdch_process_set_config, CONFIG_PL2303_WRITE2));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2210,7 +2206,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
case CONFIG_PL2303_READ5:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, enum_buf, cdch_process_set_config, CONFIG_PL2303_READ6));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, cdch_process_set_config, CONFIG_PL2303_READ6));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2218,7 +2214,7 @@ static bool pl2303_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer)
case CONFIG_PL2303_READ6:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.type != PL2303_TYPE_HXN) {
- TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, enum_buf, cdch_process_set_config, CONFIG_PL2303_WRITE3));
+ TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, cdch_process_set_config, CONFIG_PL2303_WRITE3));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c
index 0da984f4a..1f76dfcc7 100644
--- a/src/class/mtp/mtp_device.c
+++ b/src/class/mtp/mtp_device.c
@@ -441,9 +441,16 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
threshold = CFG_TUD_MTP_EP_BUFSIZE;
}
- // Check completion: ZLP, short packet, or total length reached
- const bool is_complete =
- (xferred_bytes == 0 || xferred_bytes < threshold || p_mtp->xferred_len >= p_mtp->total_len);
+ // Check completion for IN and OUT separately
+ bool is_complete;
+ if (is_data_in) {
+ // IN completion: short packet, ZLP, or reaching total_len
+ is_complete = (xferred_bytes == 0 || xferred_bytes < threshold || p_mtp->xferred_len >= p_mtp->total_len);
+ } else {
+ // OUT completion: reaching total_len or ZLP only. A short packet does NOT end the phase
+ // (an early short packet before total_len is the cancel case, not normal completion).
+ is_complete = (p_mtp->xferred_len >= p_mtp->total_len) || ((xferred_bytes == 0 && p_mtp->xferred_len > 0));
+ }
TU_LOG_DRV(" MTP Data %s CB: xferred_bytes=%lu, xferred_len/total_len=%lu/%lu, is_complete=%d\r\n",
is_data_in ? "IN" : "OUT", xferred_bytes, p_mtp->xferred_len, p_mtp->total_len, is_complete ? 1 : 0);
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index 959fc129a..cb06b89bb 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -282,6 +282,7 @@ typedef enum {
XFER_RESULT_FAILED,
XFER_RESULT_STALLED,
XFER_RESULT_TIMEOUT,
+ XFER_RESULT_ABORTED,
XFER_RESULT_INVALID
} xfer_result_t;
diff --git a/src/host/usbh.c b/src/host/usbh.c
index 2e3c93c5e..9d159985e 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -40,6 +40,14 @@
#define CFG_TUH_TASK_QUEUE_SZ 16
#endif
+#ifndef CFG_TUH_CONTROL_PENDING_QUEUE_SZ
+ #if CFG_TUH_HUB
+ #define CFG_TUH_CONTROL_PENDING_QUEUE_SZ 4
+ #else
+ #define CFG_TUH_CONTROL_PENDING_QUEUE_SZ 2
+ #endif
+#endif
+
#ifndef CFG_TUH_INTERFACE_MAX
#define CFG_TUH_INTERFACE_MAX 8
#endif
@@ -175,11 +183,11 @@ static OSAL_SPINLOCK_DEF(_usbh_spin, usbh_int_set);
OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t);
static osal_queue_t _usbh_q;
- #if CFG_TUH_HUB
+#if CFG_TUH_HUB
// Deferred attachment queue, only needed when using hub
OSAL_QUEUE_DEF(usbh_int_set, _usbh_daqdef, CFG_TUH_HUB, hcd_event_t);
static osal_queue_t _usbh_daq;
- #endif
+#endif
// Control transfers: since most controllers do not support multiple control transfers
// on multiple devices concurrently and control transfers are not used much except for
@@ -189,9 +197,9 @@ typedef struct {
tuh_xfer_cb_t complete_cb;
uintptr_t user_data;
+ volatile uint16_t actual_len;
volatile uint8_t stage;
uint8_t daddr;
- volatile uint16_t actual_len;
uint8_t failed_count;
} usbh_ctrl_xfer_info_t;
@@ -202,17 +210,32 @@ typedef struct {
} usbh_call_after_t;
typedef struct {
- uint8_t controller_id; // controller ID
+ tusb_control_request_t setup;
+ uint8_t* buffer;
+ tuh_xfer_cb_t complete_cb;
+ uintptr_t user_data;
+ uint8_t daddr;
+ uint8_t daddr_gen;
+} usbh_pending_ctrl_t;
+
+// FIFO for pending async control transfers since we only execute 1 control transfer at a time
+TU_FIFO_DEF(_usbh_pending_ctrl_q, CFG_TUH_CONTROL_PENDING_QUEUE_SZ * sizeof(usbh_pending_ctrl_t), false);
+
+typedef struct {
uint8_t enumerating_daddr; // device address of the device being enumerated
uint8_t attach_debouncing_bm; // bitmask for roothub port attach debouncing
tuh_bus_info_t dev0_bus; // bus info for dev0 in enumeration
usbh_ctrl_xfer_info_t ctrl_xfer_info; // control transfer
usbh_call_after_t call_after;
+ // Per-daddr generation counter — bumped on usbh_device_close() to identify stale pending control transfer
+ uint8_t daddr_gen[TOTAL_DEVICES + 1];
+#if CFG_TUSB_OS_HAS_SCHEDULER
+ osal_task_handle_t task_hdl; // host task handle, lazy-captured on first tuh_task_ext()
+#endif
} usbh_data_t;
-static usbh_data_t _usbh_data = {
- .controller_id = TUSB_INDEX_INVALID_8,
-};
+static uint8_t _usbh_controller_id = TUSB_INDEX_INVALID_8;
+static usbh_data_t _usbh_data;
typedef struct {
TUH_EPBUF_TYPE_DEF(tusb_control_request_t, request);
@@ -346,8 +369,11 @@ static void enum_new_device(hcd_event_t* event);
static void enum_delay_async(uintptr_t state);
static void process_remove_event(hcd_event_t *event);
static void remove_device_tree(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port);
+
static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size);
static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+static void control_xfer_dispatch_pending(void);
+static void control_xfer_complete(uint8_t daddr, xfer_result_t result);
TU_ATTR_ALWAYS_INLINE static inline usbh_device_t* get_device(uint8_t dev_addr) {
TU_VERIFY(dev_addr > 0 && dev_addr <= TOTAL_DEVICES, NULL);
@@ -364,7 +390,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event,
return true;
}
-TU_ATTR_ALWAYS_INLINE static inline void _control_set_xfer_stage(uint8_t stage) {
+TU_ATTR_ALWAYS_INLINE static inline void control_xfer_set_stage(uint8_t stage) {
if (_usbh_data.ctrl_xfer_info.stage != stage) {
(void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
_usbh_data.ctrl_xfer_info.stage = stage;
@@ -372,15 +398,6 @@ TU_ATTR_ALWAYS_INLINE static inline void _control_set_xfer_stage(uint8_t stage)
}
}
-TU_ATTR_ALWAYS_INLINE static inline bool usbh_setup_send(uint8_t daddr, const uint8_t setup_packet[8]) {
- const uint8_t rhport = usbh_get_rhport(daddr);
- const bool ret = hcd_setup_send(rhport, daddr, setup_packet);
- if (!ret) {
- _control_set_xfer_stage(CONTROL_STAGE_IDLE);
- }
- return ret;
-}
-
bool usbh_defer_func_ms_async(uint32_t ms, tusb_defer_func_t func, uintptr_t param) {
TU_ASSERT(_usbh_data.call_after.func == NULL);
TU_LOG_USBH("USBH schedule function after %u ms\r\n", (unsigned int)ms);
@@ -394,9 +411,16 @@ bool usbh_defer_func_ms_async(uint32_t ms, tusb_defer_func_t func, uintptr_t par
TU_ATTR_ALWAYS_INLINE static inline void usbh_device_close(uint8_t rhport, uint8_t daddr) {
hcd_device_close(rhport, daddr);
- // abort any ongoing control transfer
- if (daddr == _usbh_data.ctrl_xfer_info.daddr) {
- _control_set_xfer_stage(CONTROL_STAGE_IDLE);
+ // Bump the generation under the mutex so a concurrent producer in
+ // tuh_control_xfer stamps a value that is strictly monotonic w.r.t. close.
+ (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+ _usbh_data.daddr_gen[daddr]++;
+ (void) osal_mutex_unlock(_usbh_mutex);
+
+ // If this device has in-flight control xfer, complete as FAILED
+ usbh_ctrl_xfer_info_t* ctrl_info = &_usbh_data.ctrl_xfer_info;
+ if (daddr == ctrl_info->daddr && ctrl_info->stage != CONTROL_STAGE_IDLE) {
+ control_xfer_complete(daddr, XFER_RESULT_FAILED);
}
// invalidate if enumerating
@@ -458,7 +482,7 @@ tusb_speed_t tuh_speed_get(uint8_t daddr) {
}
bool tuh_rhport_is_active(uint8_t rhport) {
- return _usbh_data.controller_id == rhport;
+ return _usbh_controller_id == rhport;
}
bool tuh_rhport_reset_bus(uint8_t rhport, bool active) {
@@ -485,7 +509,7 @@ static void clear_device(usbh_device_t* dev) {
}
bool tuh_inited(void) {
- return _usbh_data.controller_id != TUSB_INDEX_INVALID_8;
+ return _usbh_controller_id != TUSB_INDEX_INVALID_8;
}
bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
@@ -547,7 +571,7 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
tu_memclr(_usbh_devices, sizeof(_usbh_devices));
tu_memclr(&_usbh_data, sizeof(_usbh_data));
- _usbh_data.controller_id = TUSB_INDEX_INVALID_8;
+ _usbh_controller_id = TUSB_INDEX_INVALID_8;
_usbh_data.enumerating_daddr = TUSB_INDEX_INVALID_8;
for (uint8_t i = 0; i < TOTAL_DEVICES; i++) {
@@ -565,7 +589,7 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
}
// Init host controller
- _usbh_data.controller_id = rhport;
+ _usbh_controller_id = rhport;
TU_ASSERT(hcd_init(rhport, rh_init));
hcd_int_enable(rhport);
@@ -580,7 +604,7 @@ bool tuh_deinit(uint8_t rhport) {
// deinit host controller
hcd_int_disable(rhport);
TU_ASSERT(hcd_deinit(rhport));
- _usbh_data.controller_id = TUSB_INDEX_INVALID_8;
+ _usbh_controller_id = TUSB_INDEX_INVALID_8;
// remove all devices on this rhport (hub_addr = 0, hub_port = 0)
remove_device_tree(rhport, 0, 0);
@@ -604,6 +628,25 @@ bool tuh_deinit(uint8_t rhport) {
_usbh_daq = NULL;
#endif
+ // Fire FAILED cb for any queued async control xfer so callers aren't stranded.
+ usbh_pending_ctrl_t pending;
+ while (tu_fifo_read_n(&_usbh_pending_ctrl_q, &pending, sizeof(pending)) == sizeof(pending)) {
+ if (pending.complete_cb) {
+ tuh_xfer_t x = {
+ .daddr = pending.daddr,
+ .ep_addr = 0,
+ .result = XFER_RESULT_FAILED,
+ .actual_len = 0,
+ .setup = &pending.setup,
+ .buffer = pending.buffer,
+ .complete_cb = pending.complete_cb,
+ .user_data = pending.user_data,
+ };
+ pending.complete_cb(&x);
+ }
+ }
+ tu_fifo_clear(&_usbh_pending_ctrl_q);
+
#if OSAL_MUTEX_REQUIRED
// TODO make sure there is no task waiting on this mutex
osal_mutex_delete(_usbh_mutex);
@@ -629,6 +672,12 @@ bool tuh_task_event_ready(void) {
}
#endif
+ // Pending control xfer waiting for an idle slot
+ if (_usbh_data.ctrl_xfer_info.stage == CONTROL_STAGE_IDLE &&
+ !tu_fifo_empty(&_usbh_pending_ctrl_q)) {
+ return true;
+ }
+
if (_usbh_data.call_after.func) {
int32_t remain_ms = (int32_t)(_usbh_data.call_after.at_ms - tusb_time_millis_api());
if (remain_ms <= 0) {
@@ -663,6 +712,13 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
(void) in_isr; // not implemented yet
+#if CFG_TUSB_OS_HAS_SCHEDULER
+ // Save task handle on 1st run
+ if (_usbh_data.task_hdl == NULL) {
+ _usbh_data.task_hdl = osal_task_get_current_handle();
+ }
+#endif
+
// Loop until there are no more events in the queue or CFG_TUH_TASK_EVENTS_PER_RUN is reached
for (unsigned epr = 0;; epr++) {
#if CFG_TUH_TASK_EVENTS_PER_RUN > 0
@@ -695,6 +751,16 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
}
}
+ // Drain pending async control xfers. Slot transitions and dispatch are
+ // decoupled: completion / abort / device_close set stage = IDLE via
+ // control_xfer_set_stage() and the actual FIFO drain happens here in the
+ // event loop. The check is a fast non-mutex sanity gate; the dispatcher
+ // itself re-checks under the mutex.
+ if (_usbh_data.ctrl_xfer_info.stage == CONTROL_STAGE_IDLE &&
+ !tu_fifo_empty(&_usbh_pending_ctrl_q)) {
+ control_xfer_dispatch_pending();
+ }
+
hcd_event_t event;
#if CFG_TUH_HUB
@@ -818,73 +884,179 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
// Control transfer
//--------------------------------------------------------------------+
-static void _control_blocking_complete_cb(tuh_xfer_t* xfer) {
- // update result
- *((xfer_result_t*) xfer->user_data) = xfer->result;
+// Carries both fields the sync waiter cares about — capturing from xfer_temp
+// (snapshot taken before release_slot resets ctrl_info for the next pending
+// entry) so the waiter sees this xfer's data, not the next dispatched one's.
+typedef struct {
+ volatile xfer_result_t result;
+ volatile uint32_t actual_len;
+} control_xfer_sync_param_t;
+
+static void control_xfer_sync_complete(tuh_xfer_t* xfer) {
+ control_xfer_sync_param_t* s = (control_xfer_sync_param_t*) xfer->user_data;
+ s->actual_len = xfer->actual_len;
+ s->result = xfer->result;
}
// TODO timeout_ms is not supported yet
bool tuh_control_xfer (tuh_xfer_t* xfer) {
- TU_VERIFY(xfer->ep_addr == 0 && xfer->setup); // EP0 with setup packet
const uint8_t daddr = xfer->daddr;
- TU_VERIFY(tuh_connected(daddr));
-
+ TU_VERIFY(daddr <= TOTAL_DEVICES && xfer->ep_addr == 0 && xfer->setup); // EP0 with setup packet
usbh_ctrl_xfer_info_t* ctrl_info = &_usbh_data.ctrl_xfer_info;
- TU_VERIFY(ctrl_info->stage == CONTROL_STAGE_IDLE); // pre-check to help reducing mutex lock
- (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
- bool const is_idle = (ctrl_info->stage == CONTROL_STAGE_IDLE);
- if (is_idle) {
- ctrl_info->stage = CONTROL_STAGE_SETUP;
- ctrl_info->daddr = daddr;
- ctrl_info->actual_len = 0;
- ctrl_info->failed_count = 0;
+#if CFG_TUSB_OS_HAS_SCHEDULER
+ // Sync (complete_cb == NULL) from a host-stack callback is forbidden on
+ // RTOS targets — the event-loop driver can't block on its own pending xfer
+ // (deadlock if other control xfers are queued behind). Use async with a
+ // chained cb instead. OS_NONE / OS_PICO are exempt: they have a single
+ // execution context and the recursive-drive path is the only way to wait.
+ TU_ASSERT(!(xfer->complete_cb == NULL &&
+ osal_task_get_current_handle() == _usbh_data.task_hdl));
+#endif
- ctrl_info->buffer = xfer->buffer;
- ctrl_info->complete_cb = xfer->complete_cb;
- ctrl_info->user_data = xfer->user_data;
- _usbh_epbuf.request = (*xfer->setup);
- }
- (void) osal_mutex_unlock(_usbh_mutex);
+ // Slot is single-threaded — when busy, sync callers block until it frees
+ // (blocking semantics require the result); async callers get queued in the
+ // pending FIFO and submitted by control_xfer_complete() when the slot
+ // drains. The test-and-{claim|enqueue} is one critical section so a slot
+ // that becomes IDLE between the check and the enqueue can't strand an async
+ // request in a queue nothing else drains.
+ const bool is_nonblocking = (xfer->complete_cb != NULL);
+ while (true) {
+ TU_VERIFY(tuh_connected(daddr));
+ bool claimed = false;
+ bool is_queued = false;
+ (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+ if (ctrl_info->stage == CONTROL_STAGE_IDLE) {
+ ctrl_info->stage = CONTROL_STAGE_SETUP;
+ ctrl_info->daddr = daddr;
+ ctrl_info->actual_len = 0;
+ ctrl_info->failed_count = 0;
+
+ ctrl_info->buffer = xfer->buffer;
+ ctrl_info->complete_cb = xfer->complete_cb;
+ ctrl_info->user_data = xfer->user_data;
+ _usbh_epbuf.request = (*xfer->setup);
+ claimed = true;
+ } else if (is_nonblocking) {
+ // Async + busy: queue the transfer.
+ const usbh_pending_ctrl_t entry = {
+ .setup = *xfer->setup,
+ .buffer = xfer->buffer,
+ .complete_cb = xfer->complete_cb,
+ .user_data = xfer->user_data,
+ .daddr = daddr,
+ .daddr_gen = _usbh_data.daddr_gen[daddr]
+ };
+ is_queued = tu_fifo_write_n(&_usbh_pending_ctrl_q, &entry, sizeof(entry)) == sizeof(entry);
+ }
+
+ (void) osal_mutex_unlock(_usbh_mutex);
+
+ if (claimed) {
+ break;
+ }
+
+ if (is_nonblocking) {
+ return is_queued;
+ }
- TU_VERIFY(is_idle);
+ // - OS_HAS_SCHEDULER: delay 1 ms
+ // - Otherwise: single execution context; drive the loop ourselves to progress the in-flight transfer.
+#if CFG_TUSB_OS_HAS_SCHEDULER
+ osal_task_delay(1);
+#else
+ tuh_task_ext(0, false);
+#endif
+ }
TU_LOG_USBH("[%u:%u] %s: ", usbh_get_rhport(daddr), daddr,
(xfer->setup->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME) ?
tu_str_std_request[xfer->setup->bRequest] : "Class Request");
TU_LOG_BUF_USBH(xfer->setup, 8);
- if (xfer->complete_cb != NULL) {
- TU_ASSERT(usbh_setup_send(daddr, (uint8_t const *) &_usbh_epbuf.request));
- }else {
- // blocking if complete callback is not provided
- // change callback to internal blocking, and result as user argument
- volatile xfer_result_t result = XFER_RESULT_INVALID;
-
- // use user_data to point to xfer_result_t
- ctrl_info->user_data = (uintptr_t) &result;
- ctrl_info->complete_cb = _control_blocking_complete_cb;
+ // Sync: wire control_xfer_sync_complete BEFORE submit so a fast completion
+ // event has the cb in place. control_xfer_complete() captures both result
+ // and actual_len through this cb before release_slot overwrites ctrl_info.
+ volatile control_xfer_sync_param_t sync_state;
+ if (!is_nonblocking) {
+ sync_state.result = XFER_RESULT_INVALID;
+ sync_state.actual_len = 0;
+ ctrl_info->user_data = (uintptr_t) &sync_state;
+ ctrl_info->complete_cb = control_xfer_sync_complete;
+ }
- TU_ASSERT(usbh_setup_send(daddr, (uint8_t const *) &_usbh_epbuf.request));
+ if (!hcd_setup_send(usbh_get_rhport(daddr), daddr, (uint8_t const *) &_usbh_epbuf.request)) {
+ control_xfer_set_stage(CONTROL_STAGE_IDLE);
+ return false;
+ }
- while (result == XFER_RESULT_INVALID) {
- // Note: this can be called within an callback ie. part of tuh_task()
- // therefore even with RTOS tuh_task_ext() still need to be invoked
+ if (!is_nonblocking) {
+ // No tuh_connected() escape needed: usbh_device_close() routes through
+ // control_xfer_complete(daddr, FAILED) on disconnect, which fires
+ // sync_complete and unblocks this poll.
+ while (sync_state.result == XFER_RESULT_INVALID) {
+#if CFG_TUSB_OS_HAS_SCHEDULER
+ osal_task_delay(1);
+#else
tuh_task_ext(0, false);
- // TODO probably some timeout to prevent hanged
+#endif
}
- // update transfer result, user_data is expected to point to xfer_result_t
+ // Forward to caller (xfer->user_data, if set, is a xfer_result_t pointer).
if (xfer->user_data != 0) {
- *((xfer_result_t*) xfer->user_data) = result;
+ *((xfer_result_t*) xfer->user_data) = sync_state.result;
}
- xfer->result = result;
- xfer->actual_len = ctrl_info->actual_len;
+ xfer->result = sync_state.result;
+ xfer->actual_len = sync_state.actual_len;
}
return true;
}
-static void _control_xfer_complete(uint8_t daddr, xfer_result_t result) {
+// Start control transfer from pending fifo
+static void control_xfer_dispatch_pending(void) {
+ usbh_ctrl_xfer_info_t* ctrl_info = &_usbh_data.ctrl_xfer_info;
+
+ while (true) {
+ usbh_pending_ctrl_t xfer;
+ bool has_xfer = false;
+
+ (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+ if (ctrl_info->stage == CONTROL_STAGE_IDLE &&
+ tu_fifo_read_n(&_usbh_pending_ctrl_q, &xfer, sizeof(xfer)) == sizeof(xfer)) {
+ ctrl_info->stage = CONTROL_STAGE_SETUP;
+ ctrl_info->daddr = xfer.daddr;
+ ctrl_info->actual_len = 0;
+ ctrl_info->failed_count = 0;
+ ctrl_info->buffer = xfer.buffer;
+ ctrl_info->complete_cb = xfer.complete_cb;
+ ctrl_info->user_data = xfer.user_data;
+ _usbh_epbuf.request = xfer.setup;
+ has_xfer = true;
+ }
+ (void) osal_mutex_unlock(_usbh_mutex);
+
+ if (!has_xfer) {
+ return; // nothing to do
+ }
+
+ // mismatched daddr_gen means pending transfer is stale due to the device got disconnected while in the FIFO
+ // Note: the address can be re-allocated to another device at this point.
+ if (xfer.daddr_gen == _usbh_data.daddr_gen[xfer.daddr]) {
+ TU_LOG_USBH("[%u:%u] %s: ", usbh_get_rhport(xfer.daddr), xfer.daddr,
+ (xfer.setup.bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && xfer.setup.bRequest <= TUSB_REQ_SYNCH_FRAME) ?
+ tu_str_std_request[xfer.setup.bRequest] : "Class Request");
+ TU_LOG_BUF_USBH(&xfer.setup, 8);
+ if (hcd_setup_send(usbh_get_rhport(xfer.daddr), xfer.daddr, (uint8_t const *) &_usbh_epbuf.request)) {
+ return; // transfer kicked-off, we are done
+ }
+ }
+
+ // complete callback as FAILED and continue with next pending xfer
+ control_xfer_complete(xfer.daddr, XFER_RESULT_FAILED);
+ }
+}
+
+static void control_xfer_complete(uint8_t daddr, xfer_result_t result) {
TU_LOG_USBH("\r\n");
usbh_ctrl_xfer_info_t* ctrl_info = &_usbh_data.ctrl_xfer_info;
@@ -901,7 +1073,8 @@ static void _control_xfer_complete(uint8_t daddr, xfer_result_t result) {
.user_data = ctrl_info->user_data
};
- _control_set_xfer_stage(CONTROL_STAGE_IDLE);
+ // set to IDLE before callback since cb can invoke another transfer
+ control_xfer_set_stage(CONTROL_STAGE_IDLE);
if (xfer_temp.complete_cb != NULL) {
xfer_temp.complete_cb(&xfer_temp);
@@ -915,11 +1088,17 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
tusb_control_request_t const * request = &_usbh_epbuf.request;
usbh_ctrl_xfer_info_t* ctrl_info = &_usbh_data.ctrl_xfer_info;
+ // Drop stale completions: slot already released (abort/close fired its cb)
+ // or now owns a different device's xfer (a pending entry was dispatched).
+ if (ctrl_info->stage == CONTROL_STAGE_IDLE || ctrl_info->daddr != daddr) {
+ return true;
+ }
+
switch (result) {
case XFER_RESULT_STALLED:
TU_LOG_USBH("[%u:%u] Control STALLED, xferred_bytes = %" PRIu32 "\r\n", rhport, daddr, xferred_bytes);
TU_LOG_BUF_USBH(request, 8);
- _control_xfer_complete(daddr, result);
+ control_xfer_complete(daddr, result);
break;
case XFER_RESULT_FAILED:
@@ -931,11 +1110,14 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
ctrl_info->actual_len = 0; // reset actual_len
(void) osal_mutex_unlock(_usbh_mutex);
- TU_ASSERT(usbh_setup_send(daddr, (uint8_t const *) request));
+ if (!hcd_setup_send(rhport, daddr, (uint8_t const *) request)) {
+ control_xfer_complete(daddr, XFER_RESULT_FAILED);
+ return false;
+ }
} else {
TU_LOG_USBH("[%u:%u] Control FAILED, xferred_bytes = %" PRIu32 "\r\n", rhport, daddr, xferred_bytes);
TU_LOG_BUF_USBH(request, 8);
- _control_xfer_complete(daddr, result);
+ control_xfer_complete(daddr, result);
}
break;
@@ -944,7 +1126,7 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
case CONTROL_STAGE_SETUP:
if (request->wLength > 0) {
// DATA stage: initial data toggle is always 1
- _control_set_xfer_stage(CONTROL_STAGE_DATA);
+ control_xfer_set_stage(CONTROL_STAGE_DATA);
const uint8_t ep_data = tu_edpt_addr(0, request->bmRequestType_bit.direction);
TU_ASSERT(hcd_edpt_xfer(rhport, daddr, ep_data, ctrl_info->buffer, request->wLength));
return true;
@@ -959,7 +1141,7 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
ctrl_info->actual_len = (uint16_t) xferred_bytes;
// ACK stage: toggle is always 1
- _control_set_xfer_stage(CONTROL_STAGE_ACK);
+ control_xfer_set_stage(CONTROL_STAGE_ACK);
const uint8_t ep_status = tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction);
TU_ASSERT(hcd_edpt_xfer(rhport, daddr, ep_status, NULL, 0));
break;
@@ -976,7 +1158,7 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
}
}
- _control_xfer_complete(daddr, result);
+ control_xfer_complete(daddr, result);
break;
}
@@ -1023,7 +1205,7 @@ bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) {
const usbh_ctrl_xfer_info_t* ctrl_info = &_usbh_data.ctrl_xfer_info;
TU_VERIFY(daddr == ctrl_info->daddr && ctrl_info->stage != CONTROL_STAGE_IDLE);
hcd_edpt_abort_xfer(rhport, daddr, ep_addr);
- _control_set_xfer_stage(CONTROL_STAGE_IDLE); // reset control transfer state to idle
+ control_xfer_complete(daddr, XFER_RESULT_ABORTED);
} else {
usbh_device_t* dev = get_device(daddr);
TU_VERIFY(dev);
@@ -1055,9 +1237,9 @@ uint8_t *usbh_get_enum_buf(void) {
void usbh_int_set(bool enabled) {
// TODO all host controller if multiple are used since they shared the same event queue
if (enabled) {
- hcd_int_enable(_usbh_data.controller_id);
+ hcd_int_enable(_usbh_controller_id);
} else {
- hcd_int_disable(_usbh_data.controller_id);
+ hcd_int_disable(_usbh_controller_id);
}
}
diff --git a/src/osal/osal.h b/src/osal/osal.h
index 4840463f3..69cb356d4 100644
--- a/src/osal/osal.h
+++ b/src/osal/osal.h
@@ -76,28 +76,31 @@ typedef void (*osal_task_func_t)(void* param);
/*--------------------------------------------------------------------
OSAL Porting API
Should be implemented as static inline function in osal_port.h header
- uint32_t osal_time_millis(void);
+ uint32_t osal_time_millis(void);
- void osal_spin_init(osal_spinlock_t *ctx);
- void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr)
- void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr);
+ void osal_task_delay(uint32_t msec);
+ osal_task_handle_t osal_task_get_current_handle(void);
- osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
- bool osal_semaphore_delete(osal_semaphore_t semd_hdl);
- bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);
- bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec);
- void osal_semaphore_reset(osal_semaphore_t sem_hdl);
+ void osal_spin_init(osal_spinlock_t *ctx);
+ void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr)
+ void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr);
- osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef);
- bool osal_mutex_delete(osal_mutex_t mutex_hdl)
- bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec);
- bool osal_mutex_unlock(osal_mutex_t mutex_hdl);
+ osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
+ bool osal_semaphore_delete(osal_semaphore_t semd_hdl);
+ bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);
+ bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec);
+ void osal_semaphore_reset(osal_semaphore_t sem_hdl);
- osal_queue_t osal_queue_create(osal_queue_def_t* qdef);
- bool osal_queue_delete(osal_queue_t qhdl);
- bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec);
- bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr);
- bool osal_queue_empty(osal_queue_t qhdl);
+ osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef);
+ bool osal_mutex_delete(osal_mutex_t mutex_hdl)
+ bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec);
+ bool osal_mutex_unlock(osal_mutex_t mutex_hdl);
+
+ osal_queue_t osal_queue_create(osal_queue_def_t* qdef);
+ bool osal_queue_delete(osal_queue_t qhdl);
+ bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec);
+ bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr);
+ bool osal_queue_empty(osal_queue_t qhdl);
--------------------------------------------------------------------------*/
diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h
index 898edd4ed..2f36aa9e8 100644
--- a/src/osal/osal_freertos.h
+++ b/src/osal/osal_freertos.h
@@ -83,6 +83,19 @@ typedef struct {
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
+typedef TaskHandle_t osal_task_handle_t;
+
+// Requires INCLUDE_xTaskGetCurrentTaskHandle == 1 in FreeRTOSConfig.h. FreeRTOS
+// also exposes the symbol when configUSE_MUTEXES == 1, so accept either.
+#if !defined(INCLUDE_xTaskGetCurrentTaskHandle) || (INCLUDE_xTaskGetCurrentTaskHandle == 0)
+ #if !defined(configUSE_MUTEXES) || (configUSE_MUTEXES == 0)
+ #error "TinyUSB host stack requires INCLUDE_xTaskGetCurrentTaskHandle or configUSE_MUTEXES to be enabled in FreeRTOSConfig.h"
+ #endif
+#endif
+TU_ATTR_ALWAYS_INLINE static inline osal_task_handle_t osal_task_get_current_handle(void) {
+ return xTaskGetCurrentTaskHandle();
+}
+
TU_ATTR_ALWAYS_INLINE static inline uint32_t _osal_ms2tick(uint32_t msec) {
if (msec == OSAL_TIMEOUT_WAIT_FOREVER) { return portMAX_DELAY; }
if (msec == 0) { return 0; }
diff --git a/src/osal/osal_mynewt.h b/src/osal/osal_mynewt.h
index 335d53491..d1fa77ecb 100644
--- a/src/osal/osal_mynewt.h
+++ b/src/osal/osal_mynewt.h
@@ -36,6 +36,12 @@
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
+typedef struct os_task* osal_task_handle_t;
+
+TU_ATTR_ALWAYS_INLINE static inline osal_task_handle_t osal_task_get_current_handle(void) {
+ return os_sched_get_current_task();
+}
+
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
os_time_delay( os_time_ms_to_ticks32(msec) );
}
diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h
index 7bf6029d6..e174d3518 100644
--- a/src/osal/osal_none.h
+++ b/src/osal/osal_none.h
@@ -34,6 +34,22 @@ extern "C" {
// osal_time_millis() is not provided, tusb_time_millis_api() must be implemented by user application
//--------------------------------------------------------------------+
+// TASK API
+//--------------------------------------------------------------------+
+// Bare-metal single context: return a non-NULL sentinel so equality compares true.
+typedef void* osal_task_handle_t;
+
+TU_ATTR_ALWAYS_INLINE static inline osal_task_handle_t osal_task_get_current_handle(void) {
+ return (osal_task_handle_t) 1;
+}
+
+// Bare-metal has no scheduler to yield to; this is dead code in practice because
+// callers gate it on running outside the host task, which can't happen here.
+TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
+ (void) msec;
+}
+
+//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
// Note: This implementation is designed for bare-metal single-core systems without RTOS.
diff --git a/src/osal/osal_pico.h b/src/osal/osal_pico.h
index 6a0a21bb3..364c38b01 100644
--- a/src/osal/osal_pico.h
+++ b/src/osal/osal_pico.h
@@ -39,6 +39,13 @@ extern "C" {
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
+// Bare-metal single context: return a non-NULL sentinel so equality compares true.
+typedef void* osal_task_handle_t;
+
+TU_ATTR_ALWAYS_INLINE static inline osal_task_handle_t osal_task_get_current_handle(void) {
+ return (osal_task_handle_t) 1;
+}
+
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
sleep_ms(msec);
}
diff --git a/src/osal/osal_rtthread.h b/src/osal/osal_rtthread.h
index f560281c5..a151a7d70 100644
--- a/src/osal/osal_rtthread.h
+++ b/src/osal/osal_rtthread.h
@@ -38,6 +38,12 @@ extern "C" {
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
+typedef rt_thread_t osal_task_handle_t;
+
+TU_ATTR_ALWAYS_INLINE static inline osal_task_handle_t osal_task_get_current_handle(void) {
+ return rt_thread_self();
+}
+
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
rt_thread_mdelay(msec);
}
diff --git a/src/osal/osal_rtx4.h b/src/osal/osal_rtx4.h
index e1930c96c..e5b708a2c 100644
--- a/src/osal/osal_rtx4.h
+++ b/src/osal/osal_rtx4.h
@@ -37,6 +37,12 @@ extern "C" {
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
+typedef OS_TID osal_task_handle_t;
+
+TU_ATTR_ALWAYS_INLINE static inline osal_task_handle_t osal_task_get_current_handle(void) {
+ return os_tsk_self();
+}
+
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
uint16_t hi = msec >> 16;
uint16_t lo = msec;
diff --git a/src/osal/osal_threadx.h b/src/osal/osal_threadx.h
index 6bcf9c5ab..cca4eb487 100644
--- a/src/osal/osal_threadx.h
+++ b/src/osal/osal_threadx.h
@@ -37,6 +37,11 @@ extern "C" {
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
+typedef TX_THREAD* osal_task_handle_t;
+
+TU_ATTR_ALWAYS_INLINE static inline osal_task_handle_t osal_task_get_current_handle(void) {
+ return tx_thread_identify();
+}
TU_ATTR_ALWAYS_INLINE static inline uint32_t _osal_ms2tick(uint32_t msec) {
if ( msec == TX_WAIT_FOREVER ) {
diff --git a/src/osal/osal_zephyr.h b/src/osal/osal_zephyr.h
index 900ac786c..6ea45131e 100644
--- a/src/osal/osal_zephyr.h
+++ b/src/osal/osal_zephyr.h
@@ -31,6 +31,12 @@
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
+typedef k_tid_t osal_task_handle_t;
+
+TU_ATTR_ALWAYS_INLINE static inline osal_task_handle_t osal_task_get_current_handle(void) {
+ return k_current_get();
+}
+
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
k_msleep(msec);
}
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 1d0ef45d2..e1a2f6cf2 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -73,8 +73,15 @@ typedef struct {
static dcd_data_t _dcd_data;
+// DMA receives up to 3 back-to-back SETUP packets (3 x 8 bytes), Slave mode only needs 1 packet (8 bytes)
+#if CFG_TUD_DWC2_DMA_ENABLE
+ #define DWC2_SETUP_BUFFER_SIZE 24
+#else
+ #define DWC2_SETUP_BUFFER_SIZE 8
+#endif
+
CFG_TUD_MEM_SECTION static struct {
- TUD_EPBUF_DEF(setup_packet, 8);
+ TUD_EPBUF_DEF(setup_buffer, DWC2_SETUP_BUFFER_SIZE);
} _dcd_usbbuf;
static tud_configure_dwc2_t _tud_cfg = CFG_TUD_CONFIGURE_DWC2_DEFAULT;
@@ -136,9 +143,9 @@ static void dma_setup_prepare(uint8_t rhport) {
}
}
- // Receive only 1 packet
- dwc2->epout[0].doeptsiz = (1 << DOEPTSIZ_STUPCNT_Pos) | (1 << DOEPTSIZ_PKTCNT_Pos) | (8 << DOEPTSIZ_XFRSIZ_Pos);
- dwc2->epout[0].doepdma = (uintptr_t) _dcd_usbbuf.setup_packet;
+ // Receive back-to-back setup packets
+ dwc2->epout[0].doeptsiz = (3 << DOEPTSIZ_STUPCNT_Pos);
+ dwc2->epout[0].doepdma = (uintptr_t) _dcd_usbbuf.setup_buffer;
dwc2->epout[0].doepctl |= DOEPCTL_EPENA | DOEPCTL_USBAEP;
}
@@ -793,13 +800,15 @@ static void handle_bus_reset(uint8_t rhport) {
xfer_status[0][TUSB_DIR_OUT].max_size = CFG_TUD_ENDPOINT0_SIZE;
xfer_status[0][TUSB_DIR_IN].max_size = CFG_TUD_ENDPOINT0_SIZE;
+ uint32_t gintmsk = GINTMSK_OTGINT | GINTMSK_IEPINT | GINTMSK_IISOIXFRM;
if(dma_device_enabled(dwc2)) {
+ gintmsk |= GINTMSK_OEPINT;
dma_setup_prepare(rhport);
} else {
dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
}
- dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_OEPINT | GINTMSK_IEPINT | GINTMSK_IISOIXFRM;
+ dwc2->gintmsk |= gintmsk;
}
static void handle_enum_done(uint8_t rhport) {
@@ -883,31 +892,47 @@ static void handle_rxflvl_irq(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const volatile uint32_t* rx_fifo = dwc2->fifo[0];
+ // DWC2 v3.10a (e.g. STM32L476) emits an extra EP0 RX_COMPLETE that is NOT a real OUT data transfer completion, in two
+ // situations - each flagged by a DOEPINT bit set on that word:
+ // - DOEPINT.STPKTRX (Setup Packet Received): pushed between SETUP_RX and SETUP_DONE of every control transfer.
+ // - DOEPINT.STSPHSRX (Status Phase Received for control write): pushed after the OUT data stage when the host
+ // starts the IN status phase.
+ // Both are dropped in the RX_COMPLETE case below, clearing the flag (W1C) so a latched STSPHSRX
+ // does not block the core from emitting the next SETUP_DONE. usbd still processes the real OUT data
+ // and queues the IN status ZLP itself - the core does not auto-complete the control-write status.
+ const bool quirk_v310a = (dwc2->gsnpsid == DWC2_CORE_REV_3_10a);
+
// Pop control word off FIFO
const dwc2_grxstsp_t grxstsp = {.value = dwc2->grxstsp};
+ const uint8_t packet_status = grxstsp.packet_status;
const uint8_t epnum = grxstsp.ep_ch_num;
dwc2_dep_t* epout = &dwc2->epout[epnum];
- switch (grxstsp.packet_status) {
+ switch (packet_status) {
case GRXSTS_PKTSTS_GLOBAL_OUT_NAK:
// Global OUT NAK: do nothing
break;
case GRXSTS_PKTSTS_SETUP_RX: {
// Setup packet received
- uint32_t* setup = (uint32_t*)(uintptr_t) _dcd_usbbuf.setup_packet;
+ uint32_t * setup = (uint32_t*)(uintptr_t) _dcd_usbbuf.setup_buffer;
// We can receive up to three setup packets in succession, but only the last one is valid.
setup[0] = (*rx_fifo);
setup[1] = (*rx_fifo);
break;
}
- case GRXSTS_PKTSTS_SETUP_DONE:
- // Setup packet done:
- // After popping this out, dwc2 asserts a DOEPINT_SETUP interrupt which is handled by handle_epout_irq()
+ case GRXSTS_PKTSTS_SETUP_DONE: {
+ // Pop this word causes the Setup interrupt
epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
+ epout->doepint = DOEPINT_SETUP | DOEPINT_STPKTRX; // Clear SETUP interrupt, required for core to re-write this control word
+ if (edpt_is_enabled(&dwc2->epin[0])) {
+ edpt_disable(rhport, 0x80, false);
+ }
+ dcd_event_setup_received(rhport, _dcd_usbbuf.setup_buffer, true);
break;
+ }
case GRXSTS_PKTSTS_RX_DATA: {
// Out packet received
@@ -935,41 +960,31 @@ static void handle_rxflvl_irq(uint8_t rhport) {
break;
}
- case GRXSTS_PKTSTS_RX_COMPLETE:
- // Out packet done
- // After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on
- // the specified OUT endpoint which will be handled by handle_epout_irq()
- break;
+ case GRXSTS_PKTSTS_RX_COMPLETE: {
+ // Pop this word causes the xfer complete interrupt
+ const uint32_t doepint = epout->doepint;
+ epout->doepint = DOEPINT_XFRC;
- default: break; // nothing to do
- }
-}
-
-static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
- if (doepint_bm.setup_phase_done) {
- // Cleanup previous pending EP0 IN transfer if any
- dwc2_dep_t* epin0 = &DWC2_REG(rhport)->epin[0];
- if (edpt_is_enabled(epin0)) {
- edpt_disable(rhport, 0x80, false);
- }
- dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
- return;
- }
+ // v3.10a quirk (see top of function): the extra RX_COMPLETE flagged with Setup Packet Received (STPKTRX) or
+ // Status Phase Received for control write (STSPHSRX) is not a real OUT completion. Drop it
+ if (quirk_v310a) {
+ if (doepint & (DOEPINT_STPKTRX | DOEPINT_STSPHSRX)) {
+ epout->doepint = DOEPINT_STPKTRX | DOEPINT_STSPHSRX;
+ break;
+ }
+ }
- // Normal OUT transfer complete
- if (doepint_bm.xfer_complete) {
- // only handle data skip if it is setup or status related
- // Note: even though (xfer_complete + status_phase_rx) is for buffered DMA only, for STM32L47x (dwc2 v3.00a) they
- // can is set when GRXSTS_PKTSTS_SETUP_RX is popped therefore they can bet set before/together with setup_phase_done
- if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) {
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) {
- // EP0 can only handle one packet, Schedule another packet to be received.
- edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
+ if (epnum == 0 && _dcd_data.ep0_pending[TUSB_DIR_OUT] > 0) {
+ // EP0 can only handle one packet, schedule another packet to be received.
+ edpt_schedule_packets(rhport, 0, TUSB_DIR_OUT);
} else {
dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
+ break;
}
+
+ default: break; // nothing to do
}
}
@@ -1008,13 +1023,23 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi
if (doepint_bm.setup_phase_done) {
// Cleanup previous pending EP0 IN transfer if any
- dwc2_dep_t* epin0 = &DWC2_REG(rhport)->epin[0];
+ dwc2_dep_t* epin0 = &dwc2->epin[0];
+ dwc2_dep_t* epout0 = &dwc2->epout[0];
if (edpt_is_enabled(epin0)) {
edpt_disable(rhport, 0x80, false);
}
- dma_setup_prepare(rhport);
- dcd_dcache_invalidate(_dcd_usbbuf.setup_packet, 8);
- dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
+
+ dcd_dcache_invalidate(_dcd_usbbuf.setup_buffer, sizeof(_dcd_usbbuf.setup_buffer));
+
+ // DOEPDMA0 has advanced past the last received SETUP packet; back up one packet to the latest valid one
+ // (Programming Guide v4.20a section 9.1.2.1: "DOEPDMAn-8 provides the pointer to the last valid SETUP data")
+ tusb_control_request_t *setup_packet = (tusb_control_request_t *) (uintptr_t) (epout0->doepdma - sizeof(tusb_control_request_t));
+ dcd_event_setup_received(rhport, (uint8_t*)setup_packet, true);
+
+ // Prepare EP0 for next setup if this setup has no data stage
+ if (setup_packet->wLength == 0) {
+ dma_setup_prepare(rhport);
+ }
return;
}
@@ -1035,9 +1060,8 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi
const uint16_t remain = tsiz.xfer_size;
xfer->total_len -= remain;
- // this is ZLP, so prepare EP0 for next setup
- // TODO use status phase rx
- if(epnum == 0 && xfer->total_len == 0) {
+ // prepare EP0 for next setup
+ if(epnum == 0) {
dma_setup_prepare(rhport);
}
@@ -1056,9 +1080,6 @@ static void handle_epin_dma(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepin
// EP0 can only handle one packet. Schedule another packet to be transmitted.
edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
} else {
- if(epnum == 0) {
- dma_setup_prepare(rhport);
- }
dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
}
@@ -1099,7 +1120,7 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
if (dir == TUSB_DIR_IN) {
handle_epin_slave(rhport, epnum, intr.diepint_bm);
} else {
- handle_epout_slave(rhport, epnum, intr.doepint_bm);
+ // epout is handled in handle_rxflvl_irq
}
#endif
}
@@ -1207,7 +1228,7 @@ void dcd_int_handler(uint8_t rhport) {
dwc2->gotgint = otg_int;
}
- if(gintsts & GINTSTS_SOF) {
+ if(gintsts & GINTSTS_SOF && dwc2->gintmsk & GINTMSK_SOFM) {
dwc2->gintsts = GINTSTS_SOF;
dwc2->gintmsk |= GINTMSK_USBSUSPM;
const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
@@ -1220,6 +1241,12 @@ void dcd_int_handler(uint8_t rhport) {
dcd_event_sof(rhport, frame, true);
}
+ // IN endpoint interrupt handling.
+ if (gintsts & GINTSTS_IEPINT) {
+ // IEPINT bit read-only, clear using DIEPINTn
+ handle_ep_irq(rhport, TUSB_DIR_IN);
+ }
+
#if CFG_TUD_DWC2_SLAVE_ENABLE
// RxFIFO non-empty interrupt handling.
if (gintsts & GINTSTS_RXFLVL) {
@@ -1234,17 +1261,13 @@ void dcd_int_handler(uint8_t rhport) {
}
#endif
+#if CFG_TUD_DWC2_DMA_ENABLE
// OUT endpoint interrupt handling.
if (gintsts & GINTSTS_OEPINT) {
// OEPINT is read-only, clear using DOEPINTn
handle_ep_irq(rhport, TUSB_DIR_OUT);
}
-
- // IN endpoint interrupt handling.
- if (gintsts & GINTSTS_IEPINT) {
- // IEPINT bit read-only, clear using DIEPINTn
- handle_ep_irq(rhport, TUSB_DIR_IN);
- }
+#endif
// Incomplete isochronous IN transfer interrupt handling.
if (gintsts & GINTSTS_IISOIXFR) {
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index 9ea5f33c5..84a0c6afd 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -104,6 +104,7 @@ typedef struct {
uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can
// be composed of multiple channel_xfer_start() (retry with NAK/NYET)
uint16_t fifo_bytes; // bytes written/read from/to FIFO (may not be transferred on USB bus).
+ uint8_t retry_disabled; // 1: channel was disabled to throttle a split retry (NAK in / XactErr out); re-arm on its halt
} hcd_xfer_t;
typedef struct {
@@ -1137,7 +1138,16 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
// TU_LOG1("in hcint = %02lX\r\n", hcint);
if (hcint & HCINT_HALTED) {
- if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL | HCINT_BABBLE_ERR)) {
+ if (xfer->retry_disabled) {
+ // Halt from our split-NAK throttle disable (below): re-arm the start-split, or let teardown finish
+ // if the endpoint is closing. Programming Guide 3.5 "Halting a Channel" (p73).
+ xfer->retry_disabled = 0;
+ if (xfer->closing) {
+ is_done = true;
+ } else {
+ channel_send_in_token(dwc2, channel);
+ }
+ } else if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL | HCINT_BABBLE_ERR)) {
const uint16_t remain_bytes = (uint16_t) hctsiz.xfer_size;
const uint16_t remain_packets = hctsiz.packet_count;
const uint16_t actual_len = edpt->buflen - remain_bytes;
@@ -1203,7 +1213,15 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR);
hcsplt.split_compl = 0; // restart with start-split
channel->hcsplt = hcsplt.value;
- channel_xfer_in_retry(dwc2, ch_id, hcint);
+ // Persistent split bulk/control IN NAK (e.g. idle polled endpoint): re-enabling immediately storms
+ // the ISR and starves the task. Disable + re-arm on the resulting halt to throttle (like the slave
+ // path); no frame deferral. Programming Guide 3.5 (p73) Note permits disable on NAK/FrmOvrn splits.
+ if ((hcint & HCINT_NAK) && hcsplt.split_en && !channel_is_periodic(channel->hcchar)) {
+ xfer->retry_disabled = 1;
+ channel_disable(dwc2, channel);
+ } else {
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
} else if (hcint & HCINT_FARME_OVERRUN) {
// retry start-split in next binterval
channel_xfer_in_retry(dwc2, ch_id, hcint);
@@ -1228,7 +1246,16 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
// TU_LOG1("out hcint = %02lX\r\n", hcint);
if (hcint & HCINT_HALTED) {
- if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL)) {
+ if (xfer->retry_disabled) {
+ // Halt from our split-XactErr throttle disable (below): re-issue the start-split (pointers already
+ // rewound), giving the hub TT a recovery gap. Programming Guide 3.5 "Halting a Channel" (p73).
+ xfer->retry_disabled = 0;
+ if (xfer->closing) {
+ is_done = true;
+ } else {
+ channel_xfer_start(dwc2, ch_id);
+ }
+ } else if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL)) {
is_done = true;
xfer->err_count = 0;
if (hcint & HCINT_XFER_COMPLETE) {
@@ -1251,9 +1278,17 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
xfer->result = XFER_RESULT_FAILED;
is_done = true;
} else {
- // clean up transfer so far and start again
+ // Rewind, then retry the start-split. Non-periodic SPLIT throttles via channel_disable + re-arm on
+ // the halt (immediate re-fire exhausts the retry budget; the disable gives the hub TT a recovery
+ // gap, like slave). Periodic split is excluded: channel_disable() is a no-op for it, so the halt
+ // never fires and the channel would wedge. Non-split re-inits immediately (Programming Guide 5.1.2.3).
channel_xfer_out_wrapup(dwc2, ch_id);
- channel_xfer_start(dwc2, ch_id);
+ if (hcsplt.split_en && !channel_is_periodic(channel->hcchar)) {
+ xfer->retry_disabled = 1;
+ channel_disable(dwc2, channel);
+ } else {
+ channel_xfer_start(dwc2, ch_id);
+ }
}
}
} else if (hcint & HCINT_NYET) {
@@ -1271,6 +1306,12 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
channel->hcsplt = hcsplt.value;
channel->hcchar |= HCCHAR_CHENA;
}
+ } else if ((hcint & HCINT_NAK) && hcsplt.split_en) {
+ // Split OUT NAK: rewind + retry the start-split, else the channel stalls (Programming Guide 5.1.4.2).
+ // Non-split OUT NAK is core-handled (5.1.2.2), so this is split-only.
+ xfer->err_count = 0;
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_xfer_start(dwc2, ch_id);
}
if (xfer->closing == 1) {
diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c
index a6dd5bb79..ea3b052ad 100644
--- a/src/portable/wch/dcd_ch32_usbhs.c
+++ b/src/portable/wch/dcd_ch32_usbhs.c
@@ -354,7 +354,7 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
if (dir == TUSB_DIR_OUT) {
EP_RX_CTRL(ep_num) = USBHS_EP_R_RES_STALL;
} else {
- EP_TX_LEN(0) = 0;
+ EP_TX_LEN(ep_num) = 0;
EP_TX_CTRL(ep_num) = USBHS_EP_T_RES_STALL;
}
}
diff --git a/src/tusb.c b/src/tusb.c
index 5d656fb8c..634cbc10b 100644
--- a/src/tusb.c
+++ b/src/tusb.c
@@ -497,7 +497,7 @@ char const* const tu_str_std_request[] = {
};
char const* const tu_str_xfer_result[] = {
- "OK", "FAILED", "STALLED", "TIMEOUT"
+ "OK", "FAILED", "STALLED", "TIMEOUT", "ABORTED", "INVALID"
};
#endif
diff --git a/src/tusb_option.h b/src/tusb_option.h
index 70549816f..415e083c9 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -536,6 +536,18 @@
#define CFG_TUSB_OS OPT_OS_NONE
#endif
+// 1 when CFG_TUSB_OS provides a preemptive scheduler with distinct tasks
+// (FreeRTOS, Zephyr, ThreadX, etc.); 0 when the application is single-context
+// (bare-metal OS_NONE or Pico SDK). Sync host control xfers from the host
+// task are forbidden when this is 1.
+#ifndef CFG_TUSB_OS_HAS_SCHEDULER
+ #if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO
+ #define CFG_TUSB_OS_HAS_SCHEDULER 0
+ #else
+ #define CFG_TUSB_OS_HAS_SCHEDULER 1
+ #endif
+#endif
+
#ifndef CFG_TUSB_OS_INC_PATH
#ifndef CFG_TUSB_OS_INC_PATH_DEFAULT
#define CFG_TUSB_OS_INC_PATH_DEFAULT
diff --git a/test/hil/hfp.json b/test/hil/hfp.json
index 8ba7a8f44..bb146d2fc 100644
--- a/test/hil/hfp.json
+++ b/test/hil/hfp.json
@@ -15,6 +15,10 @@
{
"name": "stm32f746disco",
"uid": "210041000C51343237303334",
+ "variant": [
+ { "name": "stm32f746disco", "flags": "" },
+ { "name": "stm32f746disco-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
+ ],
"tests": {
"device": true, "host": false, "dual": false
},
diff --git a/test/hil/hil_ci.sh b/test/hil/hil_ci.sh
index 96872e2e1..3ec907979 100644
--- a/test/hil/hil_ci.sh
+++ b/test/hil/hil_ci.sh
@@ -54,27 +54,60 @@ scp -q "$ROOT_DIR/test/hil/hil_test.py" \
"$CONFIG" \
"$REMOTE:$REMOTE_DIR/test/hil/"
-# Copy only firmware binaries (elf/bin/hex), preserving directory structure
+# Copy only firmware binaries (elf/bin/hex) plus esptool metadata
+# (config.env + flash_args needed by the esptool flasher), preserving structure
copy_board_binaries() {
local src="$1"
rsync -a --prune-empty-dirs \
- --include='*/' --include='*.elf' --include='*.bin' --include='*.hex' --exclude='*' \
+ --include='*/' --include='*.elf' --include='*.bin' --include='*.hex' \
+ --include='config.env' --include='flash_args' \
+ --exclude='*' \
"$src" "$REMOTE:$REMOTE_DIR/examples/"
}
if [ -n "$BOARD" ]; then
- BUILD_DIR="$ROOT_DIR/examples/cmake-build-$BOARD"
- if [ ! -d "$BUILD_DIR" ]; then
- echo "Error: build directory not found: $BUILD_DIR"
- echo "Build first with: cd examples && cmake -DBOARD=$BOARD -G Ninja -B cmake-build-$BOARD . && cmake --build cmake-build-$BOARD"
+ # Copy the board's build dir plus its variant dirs. Variant names come from
+ # $CONFIG (they are not required to be prefixed with the board name); the
+ # cmake-build-<BOARD>-* glob is kept as a fallback for ad-hoc local builds.
+ # Collect only dirs that actually exist, deduplicated.
+ declare -A SEEN_DIRS=()
+ BUILD_DIRS=()
+ add_build_dir() {
+ [[ -d "$1" && -z "${SEEN_DIRS[$1]:-}" ]] || return 0
+ SEEN_DIRS[$1]=1
+ BUILD_DIRS+=("$1")
+ }
+ shopt -s nullglob
+ for d in "$ROOT_DIR"/examples/cmake-build-"$BOARD" "$ROOT_DIR"/examples/cmake-build-"$BOARD"-*; do
+ add_build_dir "$d"
+ done
+ shopt -u nullglob
+ while IFS= read -r v; do
+ add_build_dir "$ROOT_DIR/examples/cmake-build-$v"
+ done < <(python3 -c '
+import json, sys
+cfg = json.load(open(sys.argv[1]))
+for b in cfg.get("boards", []):
+ if b["name"] == sys.argv[2]:
+ for v in b.get("variant") or []:
+ print(v["name"])
+' "$CONFIG" "$BOARD")
+ if [ ${#BUILD_DIRS[@]} -eq 0 ]; then
+ echo "Error: no build directory found for $BOARD under $ROOT_DIR/examples/"
+ echo "Build first with: cd examples && cmake --preset $BOARD && cmake --build --preset $BOARD"
exit 1
fi
- echo "==> Copying binaries for $BOARD"
- copy_board_binaries "$BUILD_DIR"
+ echo "==> Copying binaries for $BOARD (${#BUILD_DIRS[@]} build dir(s))"
+ for d in "${BUILD_DIRS[@]}"; do
+ copy_board_binaries "$d"
+ done
else
echo "==> Copying all built binaries"
+ # Use `%/` parameter expansion to strip the trailing slash from the glob —
+ # rsync needs the bare dir name so the per-board cmake-build-<BOARD>/ subdir
+ # is preserved on the remote (hil_test.py looks up binaries by that path).
for dir in "$ROOT_DIR"/examples/cmake-build-*/; do
- [ -d "$dir" ] && copy_board_binaries "$dir"
+ [ -d "$dir" ] && copy_board_binaries "${dir%/}"
done
fi
@@ -82,8 +115,21 @@ fi
# parameters; quoting and metacharacters in args are preserved.
CONFIG_BASENAME="$(basename "$CONFIG")"
echo "==> Running HIL test on $REMOTE"
-ssh "$REMOTE" bash -s -- "$REMOTE_DIR" "${ARGS[@]}" "test/hil/$CONFIG_BASENAME" <<'REMOTE'
+rc=0
+ssh "$REMOTE" bash -s -- "$REMOTE_DIR" "${ARGS[@]}" "test/hil/$CONFIG_BASENAME" <<'REMOTE' || rc=$?
cd -- "$1"
shift
-exec python3 -u test/hil/hil_test.py -B examples "$@"
+# Flasher CLIs live in the user bin dirs on ci.lan (esptool/idf in ~/.local/bin,
+# STM32CubeProgrammer's STM32_Programmer_CLI in ~/bin); the non-interactive shell
+# subprocess used for flashing doesn't source profile/rc, so add them explicitly.
+export PATH="$HOME/.local/bin:$HOME/bin:$PATH"
+python3 -u test/hil/hil_test.py -B examples "$@"
REMOTE
+
+# Copy the generated report back to the local checkout (best-effort; the run's
+# exit code is preserved regardless of whether a report was produced).
+scp -q "$REMOTE:$REMOTE_DIR/hil_report.md" "$ROOT_DIR/hil_report.md" \
+ && echo "==> Report copied to $ROOT_DIR/hil_report.md" \
+ || echo "==> warning: no hil_report.md copied back" >&2
+
+exit $rc
diff --git a/test/hil/hil_ci_set_matrix.py b/test/hil/hil_ci_set_matrix.py
index 2cce35ae2..baa24afb1 100644
--- a/test/hil/hil_ci_set_matrix.py
+++ b/test/hil/hil_ci_set_matrix.py
@@ -44,19 +44,19 @@ def main():
toolchain = 'arm-gcc'
build_board = f'-b {name}'
- if 'build' in board:
- if 'args' in board['build']:
- build_board += ' ' + ' '.join(f'-D{a}' for a in board['build']['args'])
- if 'flags_on' in board['build']:
- for f in board['build']['flags_on']:
- if f == '':
- append_build_arg(toolchain, build_board)
- else:
- append_build_arg(toolchain, f'{build_board} -f1 {f.replace(" ", " -f1 ")}')
- else:
- append_build_arg(toolchain, build_board)
- else:
- append_build_arg(toolchain, build_board)
+ if 'build' in board and 'args' in board['build']:
+ build_board += ' ' + ' '.join(f'-D{a}' for a in board['build']['args'])
+
+ # Each variant builds into cmake-build-<variant.name> with its raw CFLAGS.
+ # No 'variant' -> a single build named after the board.
+ variants = board.get('variant') or [{'name': name, 'flags': ''}]
+ for v in variants:
+ arg = build_board
+ if v['name'] != name:
+ arg += f' --build-name {v["name"]}'
+ for tok in v.get('flags', '').split():
+ arg += f' --cflag={tok}'
+ append_build_arg(toolchain, arg)
print(json.dumps(matrix))
diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py
index ed9ebbf1a..da13fcbaf 100755
--- a/test/hil/hil_test.py
+++ b/test/hil/hil_test.py
@@ -22,6 +22,14 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
+# Host setup:
+# - System packages: sudo apt install mtools libmtp9 alsa-utils iperf
+# mtools - read_disk_file (device/cdc_msc, device/msc_dual_lun)
+# libmtp9 - pymtp ctypes load (device/mtp); Debian 13 uses libmtp9t64
+# alsa-utils - arecord (device/audio_test_freertos)
+# iperf - throughput tests (device/net_lwip_*)
+# - Python packages: pip install -r requirements.txt
+#
# udev rules :
# ACTION=="add", SUBSYSTEM=="tty", SUBSYSTEMS=="usb", MODE="0666", PROGRAM="/bin/sh -c 'echo $$ID_SERIAL_SHORT | rev | cut -c -8 | rev'", SYMLINK+="ttyUSB_%c.%s{bInterfaceNumber}"
# ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", ENV{ID_FS_USAGE}=="filesystem", MODE="0666", PROGRAM="/bin/sh -c 'echo $$ID_SERIAL_SHORT | rev | cut -c -8 | rev'", RUN{program}+="/usr/bin/systemd-mount --no-block --automount=yes --collect $devnode /media/blkUSB_%c.%s{bInterfaceNumber}"
@@ -34,17 +42,11 @@ import re
import select
import sys
import time
-import warnings
import signal
from contextlib import redirect_stdout
from pathlib import Path
from typing import Any, TypedDict, NotRequired, cast
-# Suppress pkg_resources deprecation warning from fs module
-warnings.filterwarnings("ignore", message="pkg_resources is deprecated")
-# Suppress pyfatfs unclean unmount warning
-warnings.filterwarnings("ignore", message="Filesystem was not cleanly unmounted")
-
import serial
import subprocess
import json
@@ -52,7 +54,6 @@ import glob
import shutil
from multiprocessing import Pool, Lock
from multiprocessing import TimeoutError as MpTimeoutError
-import fs
import hashlib
import ctypes
from pymtp import MTP
@@ -64,6 +65,10 @@ STATUS_OK = "\033[32mOK\033[0m"
STATUS_FAILED = "\033[31mFailed\033[0m"
STATUS_SKIPPED = "\033[33mSkipped\033[0m"
+# Plain (non-ANSI) cell symbols for the markdown matrix report (hil_report.md).
+# A missing binary is reported as skipped too.
+REPORT_CELL = {'pass': '✅', 'fail': '❌', 'skip': '⚪'}
+
verbose = False
test_only = []
board_test = {}
@@ -117,16 +122,21 @@ class TestsCfg(TypedDict, total=False):
class BuildCfg(TypedDict, total=False):
- flags_on: list[str]
args: list[str]
+class VariantCfg(TypedDict, total=False):
+ name: str # build dir (cmake-build-<name>) and HIL report row
+ flags: str # raw CFLAGS, e.g. "-DCFG_TUD_DWC2_DMA_ENABLE=1"
+
+
class Board(TypedDict):
name: str
uid: str
tests: TestsCfg
flasher: FlasherCfg
build: NotRequired[BuildCfg]
+ variant: NotRequired[list[VariantCfg]]
class HilConfig(TypedDict):
@@ -218,7 +228,9 @@ def open_serial_dev(port: str):
while timeout > 0:
if os.path.exists(port):
try:
- ser = serial.Serial(port, baudrate=115200, timeout=5)
+ # write_timeout: a wedged device otherwise blocks ser.write() forever,
+ # hanging the worker until the pool/job timeout kills the whole run
+ ser = serial.Serial(port, baudrate=115200, timeout=5, write_timeout=5)
break
except serial.SerialException:
print(f'serial {port} not reaady {timeout} sec')
@@ -232,23 +244,24 @@ def open_serial_dev(port: str):
def read_disk_file(uid: str, lun: int, fname: str) -> bytes:
- # open_fs("fat://{dev}) require 'pip install pyfatfs'
+ # Reads a file from a FAT volume on a block device without mounting it.
+ # Requires mtools: `apt install mtools` (no pip dependency).
dev = get_disk_dev(uid, 'TinyUSB', lun)
timeout = ENUM_TIMEOUT
+ last_err = None
while timeout > 0:
if os.path.exists(dev):
- fat = fs.open_fs(f'fat://{dev}?read_only=true')
try:
- with fat.open(fname, 'rb') as f:
- data = f.read()
- finally:
- fat.close()
- assert data, f'Cannot read file {fname} from {dev}'
- return data
+ data = subprocess.check_output(
+ ['mtype', '-i', dev, f'::/{fname}'], stderr=subprocess.PIPE)
+ assert data, f'Cannot read file {fname} from {dev}'
+ return data
+ except subprocess.CalledProcessError as e:
+ last_err = e.stderr.decode(errors='replace').strip()
time.sleep(1)
timeout -= 1
- raise AssertionError(f'Storage {dev} not existed')
+ raise AssertionError(f'mtype failed on {dev}: {last_err}' if last_err else f'Storage {dev} not existed')
def open_mtp_dev(uid):
@@ -494,6 +507,19 @@ def reset_uniflash(board):
return subprocess.CompletedProcess(args=['dummy'], returncode=0)
+def flash_lm4flash(board, firmware):
+ # TI Tiva-C / Stellaris ICDI: lightweight lm4flash, resets and runs after write
+ flasher = board['flasher']
+ ret = run_cmd(f'lm4flash -s {flasher["uid"]} {flasher["args"]} {firmware}.bin')
+ return ret
+
+
+def reset_lm4flash(board):
+ # lm4flash has no reset-only mode; it resets+runs on flash, so reset is a no-op
+ flasher = board['flasher']
+ return subprocess.CompletedProcess(args=['dummy'], returncode=0)
+
+
# -------------------------------------------------------------
# Tests: dual
# -------------------------------------------------------------
@@ -788,12 +814,21 @@ def test_host_msc_file_explorer(board):
t -= 0.05
resp_text = resp.decode('utf-8', errors='ignore')
+ speed = None
for line in resp_text.splitlines():
if 'KB/s' in line:
print(f'{line.strip()} ', end='')
+ m = re.search(r'([\d.]+\s*[KMG]B/s)', line) # MSC read speed for the report cell
+ if m:
+ speed = 'rd ' + m.group(1).replace(' ', '')
break
ser.close()
+ return speed
+
+
+def test_host_msc_file_explorer_freertos(board):
+ return test_host_msc_file_explorer(board)
# -------------------------------------------------------------
@@ -948,6 +983,9 @@ def test_device_cdc_msc_throughput(board):
pass
print(f' CDC read {cdc_r} write {cdc_w}, MSC read {msc_r} write {msc_w} ', end='')
+ # compact read/write speed for the report cell, e.g. "✅ CDC 652k/422k MSC 1.1M/783k"
+ short = lambda s: (s.split()[0].rstrip('0').rstrip('.') + s.split()[-1][0]) if ' ' in s else s
+ return f'{REPORT_CELL["pass"]} CDC {short(cdc_r)}/{short(cdc_w)} MSC {short(msc_r)}/{short(msc_w)}'
def test_device_dfu(board):
@@ -1402,7 +1440,7 @@ def test_device_audio_test_freertos(board):
def test_device_hid_generic_inout(board):
uid = board['uid']
- import hid
+ import hid # cython-hidapi (pip: hidapi, apt: python3-hid)
# Find HID device by UID (VID=0xCafe)
timeout = ENUM_TIMEOUT
@@ -1418,22 +1456,23 @@ def test_device_hid_generic_inout(board):
timeout -= 1
assert dev is not None, f'HID device not found for {uid}'
- h = hid.Device(vid=dev['vendor_id'], pid=dev['product_id'], serial=uid)
-
- # Echo test: send random data and verify echo
- for size in [8, 32, 63]:
- # Report ID (0) + payload, padded to 64 bytes
- payload = bytes([random.randint(1, 255) for _ in range(size)])
- report = bytes([0]) + payload + bytes(64 - size)
- h.write(report)
- echo = h.read(64, timeout=2000)
- assert echo is not None and len(echo) >= size, (
- f'HID echo timeout or short read ({size} bytes)')
- assert bytes(echo[:size]) == payload, (
- f'HID echo wrong data ({size} bytes):\n'
- f' expected: {payload.hex()}\n received: {bytes(echo[:size]).hex()}')
-
- h.close()
+ h = hid.device()
+ h.open(dev['vendor_id'], dev['product_id'], uid)
+ try:
+ # Echo test: send random data and verify echo
+ for size in [8, 32, 63]:
+ # Report ID (0) + payload, padded to 64 bytes
+ payload = bytes([random.randint(1, 255) for _ in range(size)])
+ report = bytes([0]) + payload + bytes(64 - size)
+ h.write(report)
+ echo = h.read(64, 2000)
+ assert echo and len(echo) >= size, (
+ f'HID echo timeout or short read ({size} bytes)')
+ assert bytes(echo[:size]) == payload, (
+ f'HID echo wrong data ({size} bytes):\n'
+ f' expected: {payload.hex()}\n received: {bytes(echo[:size]).hex()}')
+ finally:
+ h.close()
# -------------------------------------------------------------
@@ -1465,32 +1504,47 @@ dual_tests = [
host_test = [
'host/cdc_msc_hid',
'host/msc_file_explorer',
+ 'host/msc_file_explorer_freertos',
'host/device_info',
]
-def test_example(board: Board, f1: str, example: str) -> int:
+def find_firmware(variant: str, example: str):
+ """Locate a built example's firmware base path (no extension) under
+ cmake-build-<variant>/<example>/. Accepts the single-config layout (firmware
+ directly in the example dir) or Ninja Multi-Config (a per-config subdir like
+ RelWithDebInfo/). Returns the base Path, or None if not built."""
+ fw_dir = TINYUSB_ROOT / build_dir / f'cmake-build-{variant}' / example
+ base = Path(example).name
+ if fw_dir.is_dir():
+ for cand in [fw_dir / base, fw_dir / 'RelWithDebInfo' / base,
+ *(p.with_suffix('') for p in sorted(fw_dir.glob(f'*/{base}.elf')))]:
+ if cand.with_suffix('.elf').exists() or cand.with_suffix('.bin').exists():
+ return cand
+ return None
+
+
+def test_example(board: Board, variant: str, example: str) -> tuple[int, str]:
"""
Test example firmware
:param board: board dict
- :param f1: flags on
+ :param variant: build variant name = build dir (cmake-build-<variant>) and report row
:param example: example name
- :return: 0 if success/skip, 1 if failed
+ :return: (err_count, status, metric) where err_count is 0 on success/skip or
+ 1 on failure, status is one of 'pass'/'fail'/'skip' (a missing binary
+ counts as 'skip'), and metric is an optional string a test returns to
+ show in its report cell instead of the pass symbol (e.g. speed)
"""
- name = board['name']
err_count = 0
+ result_status = 'fail'
+ metric = None
- f1_str = ""
- if f1 != "":
- f1_str = '-f1_' + f1.replace(' ', '_')
-
- fw_dir = TINYUSB_ROOT / build_dir / f'cmake-build-{name}{f1_str}' / example
- fw_name = fw_dir / Path(example).name
- test_name = f'{name+f1_str:40} {example:30} ...'
+ test_name = f'{variant:40} {example:30} ...'
- if not fw_dir.exists() or not ((fw_name.with_suffix('.elf')).exists() or (fw_name.with_suffix('.bin')).exists()):
+ fw_name = find_firmware(variant, example)
+ if fw_name is None:
log_line(f'{test_name} Skip (no binary)')
- return 0
+ return 0, 'skip', None
if verbose:
log_line(f'Flashing {fw_name}.elf')
@@ -1513,8 +1567,12 @@ def test_example(board: Board, f1: str, example: str) -> int:
last_detail = compact_output(attempt_out.getvalue())
if tret == 'skipped':
status = STATUS_SKIPPED
+ result_status = 'skip'
else:
status = STATUS_OK
+ result_status = 'pass'
+ # a test may return a string to show in its report cell (e.g. speed)
+ metric = tret if isinstance(tret, str) else None
msg = f'{test_name} {status}'
if last_detail:
msg += f' {last_detail}'
@@ -1557,26 +1615,27 @@ def test_example(board: Board, f1: str, example: str) -> int:
msg += f' in {time.time() - start_s:.1f}s'
log_line(msg)
- return err_count
+ return err_count, result_status, metric
def build_board(board: Board) -> tuple[str, int]:
"""Build firmware for this board via tools/build.py.
- Honors board config's build.flags_on variants and build.args defines.
- Output goes to cmake-build/cmake-build-BOARD[-f1_...]/ (tools/build.py layout)."""
+ Honors board config's variant list and build.args defines.
+ Output goes to cmake-build/cmake-build-<variant>/ (tools/build.py layout)."""
name = board['name']
bcfg = cast(BuildCfg, board.get('build', {}))
- flags_on_list = bcfg.get('flags_on', [''])
extra_defs = bcfg.get('args', [])
+ variants = board.get('variant') or [{'name': name, 'flags': ''}]
failed = 0
- for f1 in flags_on_list:
+ for v in variants:
cmd = [sys.executable, str(TINYUSB_ROOT / 'tools' / 'build.py'), '-b', name]
for d in extra_defs:
cmd += ['-D', d]
- if f1:
- for flag in f1.split():
- cmd += ['-f1', flag]
+ if v['name'] != name:
+ cmd += ['--build-name', v['name']]
+ for tok in v.get('flags', '').split():
+ cmd += [f'--cflag={tok}']
if verbose:
cmd.append('-v')
print(f' + {" ".join(cmd)}')
@@ -1586,7 +1645,7 @@ def build_board(board: Board) -> tuple[str, int]:
return name, failed
-def test_board(board: Board) -> tuple[str, int, list[str]]:
+def test_board(board: Board) -> tuple[str, int, list[str], list]:
name = board['name']
flasher = board['flasher']
@@ -1596,7 +1655,18 @@ def test_board(board: Board) -> tuple[str, int, list[str]]:
if name in board_test:
test_list = board_test[name]
elif len(test_only) > 0:
- test_list = test_only
+ # Explicit -t: filter against the board's capabilities so a device-only
+ # board doesn't try to run host/dual tests (the test functions need a
+ # `dev_attached` entry in the board config that won't exist).
+ board_tests = board.get('tests', {})
+ if 'only' in board_tests:
+ allowed = set(board_tests['only'])
+ test_list = [t for t in test_only if t in allowed]
+ else:
+ for t in test_only:
+ category = t.split('/', 1)[0]
+ if board_tests.get(category) is True:
+ test_list.append(t)
else:
if 'tests' in board:
board_tests = board['tests']
@@ -1616,22 +1686,96 @@ def test_board(board: Board) -> tuple[str, int, list[str]]:
err_count = 0
failed_tests = []
- flags_on_list = [""]
- if 'build' in board and 'flags_on' in board['build']:
- flags_on_list = board['build']['flags_on']
+ rows = [] # list of (row_label, {example: status}) — one row per build variant
+ variants = board.get('variant') or [{'name': name, 'flags': ''}]
- for f1 in flags_on_list:
+ for v in variants:
+ vname = v['name']
+ cells = {}
for test in test_list:
- ec = test_example(board, f1, test)
+ ec, status, metric = test_example(board, vname, test)
err_count += ec
+ cells[test] = metric if metric else status
if ec > 0:
failed_tests.append(test)
+ rows.append((vname, cells))
- # flash board_test last to disable board's usb (skipped when --skip-flash is set)
+ # flash board_test last to disable board's usb (skipped when --skip-flash is set);
+ # this is teardown/park, not a test — not recorded in the report
if not skip_flash:
- test_example(board, flags_on_list[0], 'device/board_test')
+ test_example(board, variants[0]['name'], 'device/board_test')
+
+ return name, err_count, sorted(set(failed_tests)), rows
+
+
+REPORT_MD = 'hil_report.md'
+REPORT_JSON = 'hil_report.json'
+
+
+def render_matrix(rows_all: list) -> str:
+ """Render rows (list of (row_label, {example: status})) as an aligned markdown
+ matrix: columns = tests (bare names) centered, boards left-aligned."""
+ canonical = device_tests + dual_tests + host_test
+ seen = set()
+ for _, cells in rows_all:
+ seen.update(cells)
+ if not seen:
+ return 'No tests were run.'
+
+ # columns: canonical order first, then any extras (e.g. from -t) alphabetically
+ columns = [t for t in canonical if t in seen]
+ columns += [t for t in sorted(seen) if t not in canonical]
+ headers = [c.rsplit('/', 1)[-1] for c in columns] # bare example name
+
+ def cell(cells, col):
+ v = cells.get(col)
+ if v is None:
+ return ''
+ return REPORT_CELL.get(v, v) # status symbol, or a metric string (e.g. speed) verbatim
+
+ board_hdr = 'Board'
+ board_w = max([len(board_hdr)] + [len(lbl) for lbl, _ in rows_all])
+ col_w = [max([len(h)] + [len(cell(cells, c)) for _, cells in rows_all])
+ for h, c in zip(headers, columns)]
- return name, err_count, sorted(set(failed_tests))
+ def line(label, values):
+ padded = [label.ljust(board_w)] + [v.center(w) for v, w in zip(values, col_w)]
+ return '| ' + ' | '.join(padded) + ' |'
+
+ header = line(board_hdr, headers)
+ sep = '| ' + '-' * board_w + ' | ' + ' | '.join(':' + '-' * (w - 2) + ':' for w in col_w) + ' |'
+ body = [line(lbl, [cell(cells, c) for c in columns]) for lbl, cells in rows_all]
+
+ legend = 'Legend: ✅ pass · ❌ fail · ⚪ skipped · blank not run'
+ return '\n'.join([header, sep] + body) + '\n\n' + legend
+
+
+def accumulate_report(mret: list, report_dir: Path, fresh: bool) -> str:
+ """Merge this run's results into hil_report.json in report_dir, then (re)write
+ the markdown matrix to hil_report.md. `fresh` (a full run, no --skip-board/-bt)
+ starts a new report; otherwise a re-run accumulates so boards/tests that
+ already passed are preserved while re-run cells are updated. Returns the md."""
+ acc = {} # ordered {row_label: {example: status}}
+ jpath = report_dir / REPORT_JSON
+ if not fresh and jpath.is_file():
+ try:
+ for entry in json.loads(jpath.read_text()).get('rows', []):
+ acc[entry['board']] = dict(entry['cells'])
+ except (ValueError, KeyError, TypeError):
+ pass # corrupt/old sidecar: start fresh
+
+ # merge this run: current cells override prior for boards/tests that ran
+ for _, _, _, rows in mret:
+ for row_label, cells in rows:
+ acc.setdefault(row_label, {}).update(cells)
+
+ report_dir.mkdir(parents=True, exist_ok=True)
+ jpath.write_text(json.dumps({'rows': [{'board': k, 'cells': v} for k, v in acc.items()]},
+ indent=2) + '\n')
+
+ md = render_matrix(list(acc.items()))
+ (report_dir / REPORT_MD).write_text(md + '\n', encoding='utf-8')
+ return md
def main() -> None:
@@ -1701,6 +1845,17 @@ def main() -> None:
print(f'Build phase done: {build_err} failed')
print('-' * 30)
+ # HIL report sidecar (hil_report.json/.md). A full run starts fresh; a re-run
+ # (--skip-board / -bt, i.e. the .skip file) accumulates so already-passed
+ # boards/tests are preserved. Clear any prior report up front on a fresh run so
+ # a crash mid-run can't leave stale results to be merged by a retry or posted.
+ report_dir = Path(os.environ.get('HIL_REPORT_DIR', '.'))
+ fresh = not (args.skip_board or args.board_test)
+ if fresh:
+ report_dir.mkdir(parents=True, exist_ok=True)
+ for f in (REPORT_JSON, REPORT_MD):
+ (report_dir / f).unlink(missing_ok=True)
+
with Pool(processes=os.cpu_count() or 1, initializer=init_worker, initargs=(Lock(),)) as pool:
async_ret = pool.map_async(test_board, config_boards)
try:
@@ -1715,14 +1870,20 @@ def main() -> None:
# and emit -bt BOARD:t1,t2 so each failed board only re-runs its own failed tests.
skip_fname = config_file.with_suffix(config_file.suffix + '.skip')
if err_count > 0:
- skip_boards += [name for name, err, _ in mret if err == 0]
+ skip_boards += [name for name, err, _, _ in mret if err == 0]
parts = [f'--skip-board {i}' for i in skip_boards]
- parts += [f'-bt {name}:{",".join(fts)}' for name, err, fts in mret if err > 0 and fts]
+ parts += [f'-bt {name}:{",".join(fts)}' for name, err, fts, _ in mret if err > 0 and fts]
with skip_fname.open('w') as f:
f.write(' '.join(parts))
elif skip_fname.exists():
skip_fname.unlink()
+ # board x test result matrix -> hil_report.md (accumulates across re-runs) + stdout
+ report = accumulate_report(mret, report_dir, fresh)
+ print()
+ print(report)
+ print(f'\nReport written to {(report_dir / REPORT_MD).resolve()}')
+
duration = time.time() - duration
print()
print("-" * 30)
diff --git a/test/hil/requirements.txt b/test/hil/requirements.txt
index ef2fecebe..ef1cf575b 100644
--- a/test/hil/requirements.txt
+++ b/test/hil/requirements.txt
@@ -1,4 +1,9 @@
-fs
-hid
-pyfatfs
+# System packages (install separately):
+# sudo apt install mtools libmtp9 alsa-utils iperf
+# mtools - read_disk_file (device/cdc_msc, device/msc_dual_lun)
+# libmtp9 - pymtp ctypes load (device/mtp); Debian 13 uses libmtp9t64
+# alsa-utils - arecord (device/audio_test_freertos)
+# iperf - throughput tests (device/net_lwip_*)
+hidapi
pyserial
+esptool
diff --git a/test/hil/tinyusb.json b/test/hil/tinyusb.json
index cba7677cf..afe3c4d03 100644
--- a/test/hil/tinyusb.json
+++ b/test/hil/tinyusb.json
@@ -1,14 +1,49 @@
{
"boards": [
{
+ "name": "ek_tm4c123gxl",
+ "uid": "010105186C60A110",
+ "tests": {
+ "device": true,
+ "host": false,
+ "dual": false
+ },
+ "flasher": {
+ "name": "lm4flash",
+ "uid": "0E205D19",
+ "args": "-v"
+ }
+ },
+ {
"name": "espressif_p4_function_ev",
"uid": "6055F9F98715",
- "build" : {
- "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE CFG_TUH_DWC2_DMA_ENABLE"]
- },
+ "variant": [
+ { "name": "espressif_p4_function_ev", "flags": "" },
+ { "name": "espressif_p4_function_ev-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
+ ],
"tests": {
- "only": ["device/cdc_msc_freertos", "device/hid_composite_freertos", "device/audio_test_freertos", "host/device_info"],
- "dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2002427", "is_cdc": true}]
+ "only": [
+ "device/cdc_msc_freertos",
+ "device/hid_composite_freertos",
+ "device/audio_test_freertos",
+ "host/device_info",
+ "host/msc_file_explorer_freertos"
+ ],
+ "dev_attached": [
+ {
+ "vid_pid": "1a86_55d4",
+ "serial": "52D2002427",
+ "is_cdc": true
+ },
+ {
+ "vid_pid": "21c4_0cc7",
+ "serial": "900058944CB80A53",
+ "is_msc": true,
+ "block_size": 512,
+ "block_count": 60620800,
+ "msc_inquiry": "Lexar USB Flash Drive PMAP"
+ }
+ ]
},
"flasher": {
"name": "esptool",
@@ -21,12 +56,34 @@
{
"name": "espressif_s3_devkitm",
"uid": "84F703C084E4",
- "build" : {
- "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE CFG_TUH_DWC2_DMA_ENABLE"]
- },
+ "variant": [
+ { "name": "espressif_s3_devkitm", "flags": "" },
+ { "name": "espressif_s3_devkitm-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
+ ],
"tests": {
- "only": ["device/cdc_msc_freertos", "device/hid_composite_freertos", "device/audio_test_freertos", "host/device_info"],
- "dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2005402", "is_cdc": true}]
+ "only": [
+ "device/cdc_msc_freertos",
+ "device/hid_composite_freertos",
+ "device/audio_test_freertos",
+ "host/device_info",
+ "host/msc_file_explorer_freertos"
+ ],
+ "dev_attached": [
+ {
+ "vid_pid": "1a86_55d4",
+ "serial": "52D2005402",
+ "is_cdc": true
+ },
+ {
+ "vid_pid": "048d_04d2",
+ "serial": "\u0409",
+ "is_msc": true,
+ "block_size": 512,
+ "block_count": 30720000,
+ "msc_inquiry": "General UDisk 5.00",
+ "comment": "General UDisk reports iSerialNumber=U+0409"
+ }
+ ]
},
"flasher": {
"name": "esptool",
@@ -39,7 +96,9 @@
"name": "feather_nrf52840_express",
"uid": "1F0479CD0F764471",
"tests": {
- "device": true, "host": false, "dual": false
+ "device": true,
+ "host": false,
+ "dual": false
},
"flasher": {
"name": "jlink",
@@ -51,7 +110,9 @@
"name": "max32666fthr",
"uid": "0C81464124010B20FF0A08CC2C",
"tests": {
- "device": true, "host": false, "dual": false
+ "device": true,
+ "host": false,
+ "dual": false
},
"flasher": {
"name": "openocd_adi",
@@ -71,7 +132,13 @@
"device": true,
"host": false,
"dual": true,
- "dev_attached": [{"vid_pid": "067b_2303", "serial": "0", "is_cdc": true}],
+ "dev_attached": [
+ {
+ "vid_pid": "067b_2303",
+ "serial": "0",
+ "is_cdc": true
+ }
+ ],
"comment": "pl23x"
},
"flasher": {
@@ -84,7 +151,9 @@
"name": "mimxrt1015_evk",
"uid": "DC28F865D2111D228D00B0543A70463C",
"tests": {
- "device": true, "host": false, "dual": false
+ "device": true,
+ "host": false,
+ "dual": false
},
"flasher": {
"name": "jlink",
@@ -96,9 +165,25 @@
"name": "mimxrt1064_evk",
"uid": "BAE96FB95AFA6DBB8F00005002001200",
"tests": {
- "device": true, "host": true, "dual": true,
- "dev_attached": [{"vid_pid": "10c4_ea60", "serial": "0001", "is_cdc": true}],
- "comment": "cp2102"
+ "device": true,
+ "host": true,
+ "dual": true,
+ "dev_attached": [
+ {
+ "vid_pid": "10c4_ea60",
+ "serial": "0001",
+ "is_cdc": true,
+ "comment": "cp2102"
+ },
+ {
+ "vid_pid": "21c4_0cc7",
+ "serial": "900058874D871F66",
+ "is_msc": true,
+ "block_size": 512,
+ "block_count": 60620800,
+ "msc_inquiry": "Lexar USB Flash Drive PMAP"
+ }
+ ]
},
"flasher": {
"name": "jlink",
@@ -110,7 +195,9 @@
"name": "lpcxpresso11u37",
"uid": "17121919",
"tests": {
- "device": true, "host": false, "dual": false
+ "device": true,
+ "host": false,
+ "dual": false
},
"flasher": {
"name": "jlink",
@@ -135,13 +222,30 @@
{
"name": "raspberry_pi_pico",
"uid": "E6614C311B764A37",
- "build" : {
- "flags_on": ["CFG_TUH_RPI_PIO_USB"]
- },
+ "variant": [
+ { "name": "raspberry_pi_pico", "flags": "-DCFG_TUH_RPI_PIO_USB=1" }
+ ],
"tests": {
- "device": true, "host": true, "dual": true,
- "dev_attached": [{"vid_pid": "1a86_7523", "serial": "0", "is_cdc": true}],
- "comment": "ch34x"
+ "device": true,
+ "host": true,
+ "dual": true,
+ "dev_attached": [
+ {
+ "vid_pid": "1a86_7523",
+ "serial": "0",
+ "is_cdc": true,
+ "comment": "ch34x"
+ },
+ {
+ "vid_pid": "048d_04d2",
+ "serial": "\u0409",
+ "is_msc": true,
+ "block_size": 512,
+ "block_count": 30720000,
+ "msc_inquiry": "General UDisk 5.00",
+ "comment": "General UDisk reports iSerialNumber=U+0409"
+ }
+ ]
},
"flasher": {
"name": "openocd",
@@ -153,9 +257,16 @@
"name": "raspberry_pi_pico_w",
"uid": "E6614864D35DAE36",
"tests": {
- "device": false, "host": true, "dual": false,
+ "device": false,
+ "host": true,
+ "dual": false,
"dev_attached": [
{
+ "vid_pid": "1a86_55d4",
+ "serial": "52D2002694",
+ "is_cdc": true
+ },
+ {
"vid_pid": "2008_2018",
"serial": "O20070925A002746",
"is_msc": true,
@@ -176,7 +287,9 @@
"name": "raspberry_pi_pico2",
"uid": "560AE75E1C7152C9",
"tests": {
- "device": false, "host": true, "dual": false,
+ "device": false,
+ "host": true,
+ "dual": false,
"dev_attached": [
{
"vid_pid": "0951_1603",
@@ -184,7 +297,7 @@
"is_msc": true,
"block_size": 512,
"block_count": 3987456,
- "msc_inquiry": "Kingston DataTraveler 2.0 1.0"
+ "msc_inquiry": "Kingston DataTraveler 2.0 1.00"
}
]
},
@@ -202,14 +315,24 @@
"host": true,
"dual": true,
"dev_attached": [
- {"vid_pid": "0403_6001", "serial": "0", "is_cdc": true},
- {"vid_pid": "058f_6387", "serial": "A8BEE062633D", "is_msc": true,
- "block_size": 512, "block_count": 7639040, "msc_inquiry": "Generic Flash Disk 8.07"}
+ {
+ "vid_pid": "0403_6001",
+ "serial": "0",
+ "is_cdc": true
+ },
+ {
+ "vid_pid": "058f_6387",
+ "serial": "A8BEE062633D",
+ "is_msc": true,
+ "block_size": 512,
+ "block_count": 7639040,
+ "msc_inquiry": "Generic Flash Disk 8.07"
+ }
]
},
"flasher": {
"name": "openocd",
- "uid": "E6614103E78E8324",
+ "uid": "E663AC91D3359B38",
"args": "-f interface/cmsis-dap.cfg -f target/rp2350.cfg -c \"adapter speed 5000\""
}
},
@@ -217,7 +340,9 @@
"name": "stm32f072disco",
"uid": "3A001A001357364230353532",
"tests": {
- "device": true, "host": false, "dual": false
+ "device": true,
+ "host": false,
+ "dual": false
},
"flasher": {
"name": "jlink",
@@ -227,14 +352,45 @@
"comment": "2x16 access scheme with 1KB USB SRAM"
},
{
+ "name": "stm32f407disco",
+ "uid": "30001A000647313332353735",
+ "tests": {
+ "device": true,
+ "host": false,
+ "dual": false
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "000773661813",
+ "args": "-device stm32f407vg"
+ }
+ },
+ {
"name": "stm32f723disco",
"uid": "460029001951373031313335",
- "build" : {
- "flags_on": ["", "CFG_TUH_DWC2_DMA_ENABLE"]
- },
+ "variant": [
+ { "name": "stm32f723disco", "flags": "" },
+ { "name": "stm32f723disco-DMA", "flags": "-DCFG_TUH_DWC2_DMA_ENABLE=1" }
+ ],
"tests": {
- "device": true, "host": true, "dual": false,
- "dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2003414", "is_cdc": true}]
+ "device": true,
+ "host": true,
+ "dual": false,
+ "dev_attached": [
+ {
+ "vid_pid": "1a86_55d4",
+ "serial": "52D2003414",
+ "is_cdc": true
+ },
+ {
+ "vid_pid": "21c4_0cc7",
+ "serial": "90005893730A1A63",
+ "is_msc": true,
+ "block_size": 512,
+ "block_count": 60620800,
+ "msc_inquiry": "Lexar USB Flash Drive PMAP"
+ }
+ ]
},
"flasher": {
"name": "jlink",
@@ -246,11 +402,14 @@
{
"name": "stm32h743nucleo",
"uid": "110018000951383432343236",
- "build" : {
- "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
- },
+ "variant": [
+ { "name": "stm32h743nucleo", "flags": "" },
+ { "name": "stm32h743nucleo-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
+ ],
"tests": {
- "device": true, "host": false, "dual": false
+ "device": true,
+ "host": false,
+ "dual": false
},
"flasher": {
"name": "openocd",
@@ -262,7 +421,9 @@
"name": "stm32g0b1nucleo",
"uid": "4D0038000450434E37343120",
"tests": {
- "device": true, "host": false, "dual": false
+ "device": true,
+ "host": false,
+ "dual": false
},
"flasher": {
"name": "openocd",
@@ -270,17 +431,47 @@
"args": "-f interface/stlink.cfg -f target/stm32g0x.cfg"
},
"comment": "32-bit scheme, 2KB USB SRAM"
+ },
+ {
+ "name": "stm32l476disco",
+ "uid": "3C0050001150334258343920",
+ "tests": {
+ "device": true,
+ "host": false,
+ "dual": false
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "777632258",
+ "args": "-device STM32L476VG"
+ }
+ },
+ {
+ "name": "stm32u083nucleo",
+ "uid": "300044000D5036394E373620",
+ "tests": {
+ "device": true,
+ "host": false,
+ "dual": false
+ },
+ "flasher": {
+ "name": "stlink",
+ "uid": "0668FF575457657187061314"
+ }
}
],
"boards-skip": [
{
"name": "stm32f769disco",
"uid": "21002F000F51363531383437",
- "build" : {
- "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
- },
+ "variant": [
+ { "name": "stm32f769disco", "flags": "" },
+ { "name": "stm32f769disco-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
+ ],
"tests": {
- "device": true, "host": false, "dual": false
+ "device": true,
+ "host": false,
+ "dual": false
},
"flasher": {
"name": "jlink",
@@ -292,25 +483,15 @@
"name": "nanoch32v203",
"uid": "CDAB277B0FBC03E339E339E3",
"tests": {
- "device": true, "host": false, "dual": false
+ "device": true,
+ "host": false,
+ "dual": false
},
"flasher": {
"name": "openocd_wch",
"uid": "EBCA8F0670AF",
"args": ""
}
- },
- {
- "name": "stm32f407disco",
- "uid": "30001A000647313332353735",
- "tests": {
- "device": true, "host": false, "dual": false
- },
- "flasher": {
- "name": "jlink",
- "uid": "000773661813",
- "args": "-device stm32f407vg"
- }
}
]
}
diff --git a/tools/build.py b/tools/build.py
index 3c5c3c077..86bc30d28 100755
--- a/tools/build.py
+++ b/tools/build.py
@@ -105,16 +105,14 @@ def print_build_result(board, build_target, status, duration):
# -----------------------------
# CMake
# -----------------------------
-def cmake_board(board, build_args, build_flags_on, build_targets):
+def cmake_board(board, build_args, build_name, build_cflags, build_targets):
ret = [0, 0, 0]
start_time = time.monotonic()
- build_dir = f'cmake-build/cmake-build-{board}'
+ build_dir = f'cmake-build/cmake-build-{build_name or board}'
build_flags = []
- if len(build_flags_on) > 0:
- cli_flags = ' '.join(f'-D{flag}=1' for flag in build_flags_on)
- build_flags.append(f'-DCFLAGS_CLI={cli_flags}')
- build_dir += '-f1_' + '_'.join(build_flags_on)
+ if build_cflags:
+ build_flags.append('-DCFLAGS_CLI=' + ' '.join(build_cflags))
family = find_family(board)
if family == 'espressif':
@@ -194,13 +192,13 @@ def make_board(board, build_args, build_targets):
# -----------------------------
# Build Family
# -----------------------------
-def build_boards_list(boards, build_defines, build_system, build_flags_on, build_targets):
+def build_boards_list(boards, build_defines, build_system, build_name, build_cflags, build_targets):
ret = [0, 0, 0]
for b in boards:
r = [0, 0, 0]
if build_system == 'cmake':
build_args = [f'-D{d}' for d in build_defines]
- r = cmake_board(b, build_args, build_flags_on, build_targets)
+ r = cmake_board(b, build_args, build_name, build_cflags, build_targets)
elif build_system == 'make':
build_args = ' '.join(f'{d}' for d in build_defines)
r = make_board(b, build_args, build_targets)
@@ -261,7 +259,10 @@ def main():
parser.add_argument('-t', '--toolchain', default='gcc', help='Toolchain to use, default is gcc')
parser.add_argument('-s', '--build-system', default='cmake', help='Build system to use, default is cmake')
parser.add_argument('-D', '--define-symbol', action='append', default=[], help='Define to pass to build system')
- parser.add_argument('-f1', '--build-flags-on', action='append', default=[], help='Build flag to pass to build system')
+ parser.add_argument('--build-name', default=None,
+ help='Override build dir name (cmake-build-<name>); default is the board name. Used for HIL variants.')
+ parser.add_argument('--cflag', action='append', default=[],
+ help='Raw compiler flag appended to CFLAGS_CLI, e.g. --cflag=-DCFG_TUD_DWC2_DMA_ENABLE=1 (repeatable)')
parser.add_argument('--one-random', action='store_true', default=False,
help='Build only one random board of each specified family')
parser.add_argument('--one-first', action='store_true', default=False,
@@ -277,7 +278,8 @@ def main():
toolchain = args.toolchain
build_system = args.build_system
build_defines = args.define_symbol
- build_flags_on = args.build_flags_on
+ build_name = args.build_name
+ build_cflags = args.cflag
one_random = args.one_random
one_first = args.one_first
build_targets = args.target if args.target else ['all']
@@ -290,6 +292,12 @@ def main():
print("Please specify families or board to build")
return 1
+ # --build-name renames the single shared build dir, so building more than one
+ # board with it would clobber/mix artifacts
+ if build_name and (len(families) > 0 or len(boards) != 1):
+ print("--build-name requires exactly one board (-b) and no families")
+ return 1
+
print(build_separator)
print(build_format.format('Board', 'Target', '\033[39mResult\033[0m', 'Time'))
total_time = time.monotonic()
@@ -310,7 +318,7 @@ def main():
all_boards.extend(get_family_boards(f, one_random, one_first))
# build all boards
- result = build_boards_list(all_boards, build_defines, build_system, build_flags_on, build_targets)
+ result = build_boards_list(all_boards, build_defines, build_system, build_name, build_cflags, build_targets)
total_time = time.monotonic() - total_time
print(build_separator)
diff --git a/tools/codespell/ignore-words.txt b/tools/codespell/ignore-words.txt
index 7ce778fab..0b1aa284a 100644
--- a/tools/codespell/ignore-words.txt
+++ b/tools/codespell/ignore-words.txt
@@ -6,6 +6,7 @@ fro
hsi
inout
mot
+ore
pris
ptd
ser
diff --git a/tools/gen_presets.py b/tools/gen_presets.py
index 94a9361db..6f32976a7 100755
--- a/tools/gen_presets.py
+++ b/tools/gen_presets.py
@@ -32,7 +32,7 @@ def main():
"hidden": True,
"description": r"Configure preset for the ${presetName} board",
"generator": "Ninja Multi-Config",
- "binaryDir": r"${sourceDir}/build/${presetName}",
+ "binaryDir": r"${sourceDir}/cmake-build-${presetName}",
"cacheVariables": {
"CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo",
"BOARD": r"${presetName}"
@@ -41,7 +41,7 @@ def main():
"hidden": True,
"description": r"Configure preset for the ${presetName} board",
"generator": "Ninja",
- "binaryDir": r"${sourceDir}/build/${presetName}",
+ "binaryDir": r"${sourceDir}/cmake-build-${presetName}",
"cacheVariables": {
"BOARD": r"${presetName}"
}}]
diff --git a/tools/get_deps.py b/tools/get_deps.py
index 50497fc71..60be5d89e 100755
--- a/tools/get_deps.py
+++ b/tools/get_deps.py
@@ -372,6 +372,8 @@ def main():
parser.add_argument('-b', '--board', action='append', default=[], help='Boards to fetch')
parser.add_argument('-D', '--define', action='append', default=[], help='Have no effect')
parser.add_argument('-f1', '--build-flags-on', action='append', default=[], help='Have no effect')
+ parser.add_argument('--build-name', default=None, help='Have no effect')
+ parser.add_argument('--cflag', action='append', default=[], help='Have no effect')
args = parser.parse_args()
families = args.families
diff --git a/tools/metrics.py b/tools/metrics.py
index f624f382f..b7aa056e4 100644
--- a/tools/metrics.py
+++ b/tools/metrics.py
@@ -384,7 +384,7 @@ def render_combine_table(json_data, sort_order='name+'):
def write_combine_markdown(json_data, path, sort_order='name+', title="TinyUSB Average Code Size Metrics"):
"""Write averaged size data to a markdown file."""
- md_lines = [f"# {title}", ""]
+ md_lines = [f"## {title}", ""]
md_lines.extend(render_combine_table(json_data, sort_order))
md_lines.append("")
@@ -400,7 +400,7 @@ def write_combine_markdown(json_data, path, sort_order='name+', title="TinyUSB A
def write_compare_markdown(comparison, path, sort_order='size'):
"""Write comparison data to markdown file."""
md_lines = [
- "# Size Difference Report",
+ "## Size Difference Report",
"",
"Because TinyUSB code size varies by port and configuration, the metrics below represent the averaged totals across all example builds.",
"",
@@ -415,7 +415,7 @@ def write_compare_markdown(comparison, path, sort_order='size'):
md_lines.append(f"<details><summary>{title}</summary>")
md_lines.append("")
else:
- md_lines.append(f"## {title}")
+ md_lines.append(f"### {title}")
md_lines.extend(render_compare_table(_build_rows(rows, sort_order), include_sum=True))
md_lines.append("")